home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #11 / Amiga Plus CD - 2004 - No. 11.iso / AmiSoft / Comm / www / tidy_os4.lha / tidy / src / lexer.c < prev    next >
C/C++ Source or Header  |  2004-07-25  |  104KB  |  3,656 lines

  1. /* lexer.c -- Lexer for html parser
  2.   
  3.   (c) 1998-2004 (W3C) MIT, ERCIM, Keio University
  4.   See tidy.h for the copyright notice.
  5.   
  6.   CVS Info :
  7.  
  8.     $Author: hoehrmann $ 
  9.     $Date: 2004/06/18 21:10:30 $ 
  10.     $Revision: 1.153 $ 
  11.  
  12. */
  13.  
  14. /*
  15.   Given a file stream fp it returns a sequence of tokens.
  16.  
  17.      GetToken(fp) gets the next token
  18.      UngetToken(fp) provides one level undo
  19.  
  20.   The tags include an attribute list:
  21.  
  22.     - linked list of attribute/value nodes
  23.     - each node has 2 NULL-terminated strings.
  24.     - entities are replaced in attribute values
  25.  
  26.   white space is compacted if not in preformatted mode
  27.   If not in preformatted mode then leading white space
  28.   is discarded and subsequent white space sequences
  29.   compacted to single space characters.
  30.  
  31.   If XmlTags is no then Tag names are folded to upper
  32.   case and attribute names to lower case.
  33.  
  34.  Not yet done:
  35.     -   Doctype subset and marked sections
  36. */
  37.  
  38. #include "tidy-int.h"
  39. #include "lexer.h"
  40. #include "parser.h"
  41. #include "entities.h"
  42. #include "streamio.h"
  43. #include "message.h"
  44. #include "tmbstr.h"
  45. #include "clean.h"
  46. #include "utf8.h"
  47. #include "streamio.h"
  48.  
  49. /* Forward references
  50. */
  51. /* swallows closing '>' */
  52. AttVal *ParseAttrs( TidyDocImpl* doc, Bool *isempty );
  53.  
  54. static tmbstr ParseAttribute( TidyDocImpl* doc, Bool* isempty, 
  55.                              Node **asp, Node **php );
  56.  
  57. static tmbstr ParseValue( TidyDocImpl* doc, ctmbstr name, Bool foldCase,
  58.                          Bool *isempty, int *pdelim );
  59.  
  60. static Node *ParseDocTypeDecl(TidyDocImpl* doc);
  61.  
  62. static void AddAttrToList( AttVal** list, AttVal* av );
  63.  
  64. /* used to classify characters for lexical purposes */
  65. #define MAP(c) ((unsigned)c < 128 ? lexmap[(unsigned)c] : 0)
  66. uint lexmap[128];
  67.  
  68. #define IsValidXMLAttrName(name) IsValidXMLID(name)
  69. #define IsValidXMLElemName(name) IsValidXMLID(name)
  70.  
  71. struct _doctypes
  72. {
  73.     uint score;
  74.     uint vers;
  75.     ctmbstr name;
  76.     ctmbstr fpi;
  77.     ctmbstr si;
  78. } const W3C_Doctypes[] =
  79. {
  80.   {  2, HT20, "HTML 2.0",               "-//IETF//DTD HTML 2.0//EN",              NULL,                                                       },
  81.   {  2, HT20, "HTML 2.0",               "-//IETF//DTD HTML//EN",                  NULL,                                                       },
  82.   {  2, HT20, "HTML 2.0",               "-//W3C//DTD HTML 2.0//EN",               NULL,                                                       },
  83.   {  1, HT32, "HTML 3.2",               "-//W3C//DTD HTML 3.2//EN",               NULL,                                                       },
  84.   {  1, HT32, "HTML 3.2",               "-//W3C//DTD HTML 3.2 Final//EN",         NULL,                                                       },
  85.   {  1, HT32, "HTML 3.2",               "-//W3C//DTD HTML 3.2 Draft//EN",         NULL,                                                       },
  86.   {  6, H40S, "HTML 4.0 Strict",        "-//W3C//DTD HTML 4.0//EN",               "http://www.w3.org/TR/REC-html40/strict.dtd"                },
  87.   {  8, H40T, "HTML 4.0 Transitional",  "-//W3C//DTD HTML 4.0 Transitional//EN",  "http://www.w3.org/TR/REC-html40/loose.dtd"                 },
  88.   {  7, H40F, "HTML 4.0 Frameset",      "-//W3C//DTD HTML 4.0 Frameset//EN",      "http://www.w3.org/TR/REC-html40/frameset.dtd"              },
  89.   {  3, H41S, "HTML 4.01 Strict",       "-//W3C//DTD HTML 4.01//EN",              "http://www.w3.org/TR/html4/strict.dtd"                     },
  90.   {  5, H41T, "HTML 4.01 Transitional", "-//W3C//DTD HTML 4.01 Transitional//EN", "http://www.w3.org/TR/html4/loose.dtd"                      },
  91.   {  4, H41F, "HTML 4.01 Frameset",     "-//W3C//DTD HTML 4.01 Frameset//EN",     "http://www.w3.org/TR/html4/frameset.dtd"                   },
  92.   {  9, X10S, "XHTML 1.0 Strict",       "-//W3C//DTD XHTML 1.0 Strict//EN",       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"         },
  93.   { 11, X10T, "XHTML 1.0 Transitional", "-//W3C//DTD XHTML 1.0 Transitional//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"   },
  94.   { 10, X10F, "XHTML 1.0 Frameset",     "-//W3C//DTD XHTML 1.0 Frameset//EN",     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"       },
  95.   { 12, XH11, "XHTML 1.1",              "-//W3C//DTD XHTML 1.1//EN",              "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"              },
  96.   { 13, XB10, "XHTML Basic 1.0",        "-//W3C//DTD XHTML Basic 1.0//EN",        "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd"        },
  97.  
  98.   /* reminder to add XHTML Print 1.0 support, see http://www.w3.org/TR/xhtml-print */
  99. #if 0
  100.   { 14, XP10, "XHTML Print 1.0",        "-//W3C//DTD XHTML-Print 1.0//EN",         "http://www.w3.org/MarkUp/DTD/xhtml-print10.dtd"           },
  101.   { 14, XP10, "XHTML Print 1.0",        "-//PWG//DTD XHTML-Print 1.0//EN",         "http://www.xhtml-print.org/xhtml-print/xhtml-print10.dtd" },
  102. #endif
  103.   /* final entry */
  104.   {  0,    0, NULL,                     NULL,                                     NULL                                                        }
  105. };
  106.  
  107. int HTMLVersion(TidyDocImpl* doc)
  108. {
  109.     uint i;
  110.     uint j = 0;
  111.     uint score = 0;
  112.     uint vers = doc->lexer->versions;
  113.     uint dtver = doc->lexer->doctype;
  114.     TidyDoctypeModes dtmode = (TidyDoctypeModes)cfg(doc, TidyDoctypeMode);
  115.     Bool xhtml = (cfgBool(doc, TidyXmlOut) || doc->lexer->isvoyager) &&
  116.                  !cfgBool(doc, TidyHtmlOut);
  117.     Bool html4 = dtmode == TidyDoctypeStrict || dtmode == TidyDoctypeLoose || VERS_FROM40 & dtver;
  118.  
  119.     for (i = 0; W3C_Doctypes[i].name; ++i)
  120.     {
  121.         if ((xhtml && !(VERS_XHTML & W3C_Doctypes[i].vers)) ||
  122.             (html4 && !(VERS_FROM40 & W3C_Doctypes[i].vers)))
  123.             continue;
  124.  
  125.         if (vers & W3C_Doctypes[i].vers &&
  126.             (W3C_Doctypes[i].score < score || !score))
  127.         {
  128.             score = W3C_Doctypes[i].score;
  129.             j = i;
  130.         }
  131.     }
  132.  
  133.     if (score)
  134.         return W3C_Doctypes[j].vers;
  135.  
  136.     return VERS_UNKNOWN;
  137. }
  138.  
  139. ctmbstr GetFPIFromVers(uint vers)
  140. {
  141.     uint i;
  142.  
  143.     for (i = 0; W3C_Doctypes[i].name; ++i)
  144.         if (W3C_Doctypes[i].vers == vers)
  145.             return W3C_Doctypes[i].fpi;
  146.  
  147.     return NULL;
  148. }
  149.  
  150. static ctmbstr GetSIFromVers(uint vers)
  151. {
  152.     uint i;
  153.  
  154.     for (i = 0; W3C_Doctypes[i].name; ++i)
  155.         if (W3C_Doctypes[i].vers == vers)
  156.             return W3C_Doctypes[i].si;
  157.  
  158.     return NULL;
  159. }
  160.  
  161. static ctmbstr GetNameFromVers(uint vers)
  162. {
  163.     uint i;
  164.  
  165.     for (i = 0; W3C_Doctypes[i].name; ++i)
  166.         if (W3C_Doctypes[i].vers == vers)
  167.             return W3C_Doctypes[i].name;
  168.  
  169.     return NULL;
  170. }
  171.  
  172. static uint GetVersFromFPI(ctmbstr fpi)
  173. {
  174.     uint i;
  175.  
  176.     for (i = 0; W3C_Doctypes[i].name; ++i)
  177.         if (tmbstrcasecmp(W3C_Doctypes[i].fpi, fpi) == 0)
  178.             return W3C_Doctypes[i].vers;
  179.  
  180.     return 0;
  181. }
  182.  
  183. /* everything is allowed in proprietary version of HTML */
  184. /* this is handled here rather than in the tag/attr dicts */
  185. void ConstrainVersion(TidyDocImpl* doc, uint vers)
  186. {
  187.     doc->lexer->versions &= (vers | VERS_PROPRIETARY);
  188. }
  189.  
  190. Bool IsWhite(uint c)
  191. {
  192.     uint map = MAP(c);
  193.  
  194.     return (Bool)(map & white);
  195. }
  196.  
  197. Bool IsNewline(uint c)
  198. {
  199.     uint map = MAP(c);
  200.     return (Bool)(map & newline);
  201. }
  202.  
  203. Bool IsDigit(uint c)
  204. {
  205.     uint map;
  206.  
  207.     map = MAP(c);
  208.  
  209.     return (Bool)(map & digit);
  210. }
  211.  
  212. Bool IsLetter(uint c)
  213. {
  214.     uint map;
  215.  
  216.     map = MAP(c);
  217.  
  218.     return (Bool)(map & letter);
  219. }
  220.  
  221. Bool IsNamechar(uint c)
  222. {
  223.     uint map = MAP(c);
  224.     return (Bool)(map & namechar);
  225. }
  226.  
  227. Bool IsXMLLetter(uint c)
  228. {
  229.     return ((c >= 0x41 && c <= 0x5a) ||
  230.         (c >= 0x61 && c <= 0x7a) ||
  231.         (c >= 0xc0 && c <= 0xd6) ||
  232.         (c >= 0xd8 && c <= 0xf6) ||
  233.         (c >= 0xf8 && c <= 0xff) ||
  234.         (c >= 0x100 && c <= 0x131) ||
  235.         (c >= 0x134 && c <= 0x13e) ||
  236.         (c >= 0x141 && c <= 0x148) ||
  237.         (c >= 0x14a && c <= 0x17e) ||
  238.         (c >= 0x180 && c <= 0x1c3) ||
  239.         (c >= 0x1cd && c <= 0x1f0) ||
  240.         (c >= 0x1f4 && c <= 0x1f5) ||
  241.         (c >= 0x1fa && c <= 0x217) ||
  242.         (c >= 0x250 && c <= 0x2a8) ||
  243.         (c >= 0x2bb && c <= 0x2c1) ||
  244.         c == 0x386 ||
  245.         (c >= 0x388 && c <= 0x38a) ||
  246.         c == 0x38c ||
  247.         (c >= 0x38e && c <= 0x3a1) ||
  248.         (c >= 0x3a3 && c <= 0x3ce) ||
  249.         (c >= 0x3d0 && c <= 0x3d6) ||
  250.         c == 0x3da ||
  251.         c == 0x3dc ||
  252.         c == 0x3de ||
  253.         c == 0x3e0 ||
  254.         (c >= 0x3e2 && c <= 0x3f3) ||
  255.         (c >= 0x401 && c <= 0x40c) ||
  256.         (c >= 0x40e && c <= 0x44f) ||
  257.         (c >= 0x451 && c <= 0x45c) ||
  258.         (c >= 0x45e && c <= 0x481) ||
  259.         (c >= 0x490 && c <= 0x4c4) ||
  260.         (c >= 0x4c7 && c <= 0x4c8) ||
  261.         (c >= 0x4cb && c <= 0x4cc) ||
  262.         (c >= 0x4d0 && c <= 0x4eb) ||
  263.         (c >= 0x4ee && c <= 0x4f5) ||
  264.         (c >= 0x4f8 && c <= 0x4f9) ||
  265.         (c >= 0x531 && c <= 0x556) ||
  266.         c == 0x559 ||
  267.         (c >= 0x561 && c <= 0x586) ||
  268.         (c >= 0x5d0 && c <= 0x5ea) ||
  269.         (c >= 0x5f0 && c <= 0x5f2) ||
  270.         (c >= 0x621 && c <= 0x63a) ||
  271.         (c >= 0x641 && c <= 0x64a) ||
  272.         (c >= 0x671 && c <= 0x6b7) ||
  273.         (c >= 0x6ba && c <= 0x6be) ||
  274.         (c >= 0x6c0 && c <= 0x6ce) ||
  275.         (c >= 0x6d0 && c <= 0x6d3) ||
  276.         c == 0x6d5 ||
  277.         (c >= 0x6e5 && c <= 0x6e6) ||
  278.         (c >= 0x905 && c <= 0x939) ||
  279.         c == 0x93d ||
  280.         (c >= 0x958 && c <= 0x961) ||
  281.         (c >= 0x985 && c <= 0x98c) ||
  282.         (c >= 0x98f && c <= 0x990) ||
  283.         (c >= 0x993 && c <= 0x9a8) ||
  284.         (c >= 0x9aa && c <= 0x9b0) ||
  285.         c == 0x9b2 ||
  286.         (c >= 0x9b6 && c <= 0x9b9) ||
  287.         (c >= 0x9dc && c <= 0x9dd) ||
  288.         (c >= 0x9df && c <= 0x9e1) ||
  289.         (c >= 0x9f0 && c <= 0x9f1) ||
  290.         (c >= 0xa05 && c <= 0xa0a) ||
  291.         (c >= 0xa0f && c <= 0xa10) ||
  292.         (c >= 0xa13 && c <= 0xa28) ||
  293.         (c >= 0xa2a && c <= 0xa30) ||
  294.         (c >= 0xa32 && c <= 0xa33) ||
  295.         (c >= 0xa35 && c <= 0xa36) ||
  296.         (c >= 0xa38 && c <= 0xa39) ||
  297.         (c >= 0xa59 && c <= 0xa5c) ||
  298.         c == 0xa5e ||
  299.         (c >= 0xa72 && c <= 0xa74) ||
  300.         (c >= 0xa85 && c <= 0xa8b) ||
  301.         c == 0xa8d ||
  302.         (c >= 0xa8f && c <= 0xa91) ||
  303.         (c >= 0xa93 && c <= 0xaa8) ||
  304.         (c >= 0xaaa && c <= 0xab0) ||
  305.         (c >= 0xab2 && c <= 0xab3) ||
  306.         (c >= 0xab5 && c <= 0xab9) ||
  307.         c == 0xabd ||
  308.         c == 0xae0 ||
  309.         (c >= 0xb05 && c <= 0xb0c) ||
  310.         (c >= 0xb0f && c <= 0xb10) ||
  311.         (c >= 0xb13 && c <= 0xb28) ||
  312.         (c >= 0xb2a && c <= 0xb30) ||
  313.         (c >= 0xb32 && c <= 0xb33) ||
  314.         (c >= 0xb36 && c <= 0xb39) ||
  315.         c == 0xb3d ||
  316.         (c >= 0xb5c && c <= 0xb5d) ||
  317.         (c >= 0xb5f && c <= 0xb61) ||
  318.         (c >= 0xb85 && c <= 0xb8a) ||
  319.         (c >= 0xb8e && c <= 0xb90) ||
  320.         (c >= 0xb92 && c <= 0xb95) ||
  321.         (c >= 0xb99 && c <= 0xb9a) ||
  322.         c == 0xb9c ||
  323.         (c >= 0xb9e && c <= 0xb9f) ||
  324.         (c >= 0xba3 && c <= 0xba4) ||
  325.         (c >= 0xba8 && c <= 0xbaa) ||
  326.         (c >= 0xbae && c <= 0xbb5) ||
  327.         (c >= 0xbb7 && c <= 0xbb9) ||
  328.         (c >= 0xc05 && c <= 0xc0c) ||
  329.         (c >= 0xc0e && c <= 0xc10) ||
  330.         (c >= 0xc12 && c <= 0xc28) ||
  331.         (c >= 0xc2a && c <= 0xc33) ||
  332.         (c >= 0xc35 && c <= 0xc39) ||
  333.         (c >= 0xc60 && c <= 0xc61) ||
  334.         (c >= 0xc85 && c <= 0xc8c) ||
  335.         (c >= 0xc8e && c <= 0xc90) ||
  336.         (c >= 0xc92 && c <= 0xca8) ||
  337.         (c >= 0xcaa && c <= 0xcb3) ||
  338.         (c >= 0xcb5 && c <= 0xcb9) ||
  339.         c == 0xcde ||
  340.         (c >= 0xce0 && c <= 0xce1) ||
  341.         (c >= 0xd05 && c <= 0xd0c) ||
  342.         (c >= 0xd0e && c <= 0xd10) ||
  343.         (c >= 0xd12 && c <= 0xd28) ||
  344.         (c >= 0xd2a && c <= 0xd39) ||
  345.         (c >= 0xd60 && c <= 0xd61) ||
  346.         (c >= 0xe01 && c <= 0xe2e) ||
  347.         c == 0xe30 ||
  348.         (c >= 0xe32 && c <= 0xe33) ||
  349.         (c >= 0xe40 && c <= 0xe45) ||
  350.         (c >= 0xe81 && c <= 0xe82) ||
  351.         c == 0xe84 ||
  352.         (c >= 0xe87 && c <= 0xe88) ||
  353.         c == 0xe8a ||
  354.         c == 0xe8d ||
  355.         (c >= 0xe94 && c <= 0xe97) ||
  356.         (c >= 0xe99 && c <= 0xe9f) ||
  357.         (c >= 0xea1 && c <= 0xea3) ||
  358.         c == 0xea5 ||
  359.         c == 0xea7 ||
  360.         (c >= 0xeaa && c <= 0xeab) ||
  361.         (c >= 0xead && c <= 0xeae) ||
  362.         c == 0xeb0 ||
  363.         (c >= 0xeb2 && c <= 0xeb3) ||
  364.         c == 0xebd ||
  365.         (c >= 0xec0 && c <= 0xec4) ||
  366.         (c >= 0xf40 && c <= 0xf47) ||
  367.         (c >= 0xf49 && c <= 0xf69) ||
  368.         (c >= 0x10a0 && c <= 0x10c5) ||
  369.         (c >= 0x10d0 && c <= 0x10f6) ||
  370.         c == 0x1100 ||
  371.         (c >= 0x1102 && c <= 0x1103) ||
  372.         (c >= 0x1105 && c <= 0x1107) ||
  373.         c == 0x1109 ||
  374.         (c >= 0x110b && c <= 0x110c) ||
  375.         (c >= 0x110e && c <= 0x1112) ||
  376.         c == 0x113c ||
  377.         c == 0x113e ||
  378.         c == 0x1140 ||
  379.         c == 0x114c ||
  380.         c == 0x114e ||
  381.         c == 0x1150 ||
  382.         (c >= 0x1154 && c <= 0x1155) ||
  383.         c == 0x1159 ||
  384.         (c >= 0x115f && c <= 0x1161) ||
  385.         c == 0x1163 ||
  386.         c == 0x1165 ||
  387.         c == 0x1167 ||
  388.         c == 0x1169 ||
  389.         (c >= 0x116d && c <= 0x116e) ||
  390.         (c >= 0x1172 && c <= 0x1173) ||
  391.         c == 0x1175 ||
  392.         c == 0x119e ||
  393.         c == 0x11a8 ||
  394.         c == 0x11ab ||
  395.         (c >= 0x11ae && c <= 0x11af) ||
  396.         (c >= 0x11b7 && c <= 0x11b8) ||
  397.         c == 0x11ba ||
  398.         (c >= 0x11bc && c <= 0x11c2) ||
  399.         c == 0x11eb ||
  400.         c == 0x11f0 ||
  401.         c == 0x11f9 ||
  402.         (c >= 0x1e00 && c <= 0x1e9b) ||
  403.         (c >= 0x1ea0 && c <= 0x1ef9) ||
  404.         (c >= 0x1f00 && c <= 0x1f15) ||
  405.         (c >= 0x1f18 && c <= 0x1f1d) ||
  406.         (c >= 0x1f20 && c <= 0x1f45) ||
  407.         (c >= 0x1f48 && c <= 0x1f4d) ||
  408.         (c >= 0x1f50 && c <= 0x1f57) ||
  409.         c == 0x1f59 ||
  410.         c == 0x1f5b ||
  411.         c == 0x1f5d ||
  412.         (c >= 0x1f5f && c <= 0x1f7d) ||
  413.         (c >= 0x1f80 && c <= 0x1fb4) ||
  414.         (c >= 0x1fb6 && c <= 0x1fbc) ||
  415.         c == 0x1fbe ||
  416.         (c >= 0x1fc2 && c <= 0x1fc4) ||
  417.         (c >= 0x1fc6 && c <= 0x1fcc) ||
  418.         (c >= 0x1fd0 && c <= 0x1fd3) ||
  419.         (c >= 0x1fd6 && c <= 0x1fdb) ||
  420.         (c >= 0x1fe0 && c <= 0x1fec) ||
  421.         (c >= 0x1ff2 && c <= 0x1ff4) ||
  422.         (c >= 0x1ff6 && c <= 0x1ffc) ||
  423.         c == 0x2126 ||
  424.         (c >= 0x212a && c <= 0x212b) ||
  425.         c == 0x212e ||
  426.         (c >= 0x2180 && c <= 0x2182) ||
  427.         (c >= 0x3041 && c <= 0x3094) ||
  428.         (c >= 0x30a1 && c <= 0x30fa) ||
  429.         (c >= 0x3105 && c <= 0x312c) ||
  430.         (c >= 0xac00 && c <= 0xd7a3) ||
  431.         (c >= 0x4e00 && c <= 0x9fa5) ||
  432.         c == 0x3007 ||
  433.         (c >= 0x3021 && c <= 0x3029) ||
  434.         (c >= 0x4e00 && c <= 0x9fa5) ||
  435.         c == 0x3007 ||
  436.         (c >= 0x3021 && c <= 0x3029));
  437. }
  438.  
  439. Bool IsXMLNamechar(uint c)
  440. {
  441.     return (IsXMLLetter(c) ||
  442.         c == '.' || c == '_' ||
  443.         c == ':' || c == '-' ||
  444.         (c >= 0x300 && c <= 0x345) ||
  445.         (c >= 0x360 && c <= 0x361) ||
  446.         (c >= 0x483 && c <= 0x486) ||
  447.         (c >= 0x591 && c <= 0x5a1) ||
  448.         (c >= 0x5a3 && c <= 0x5b9) ||
  449.         (c >= 0x5bb && c <= 0x5bd) ||
  450.         c == 0x5bf ||
  451.         (c >= 0x5c1 && c <= 0x5c2) ||
  452.         c == 0x5c4 ||
  453.         (c >= 0x64b && c <= 0x652) ||
  454.         c == 0x670 ||
  455.         (c >= 0x6d6 && c <= 0x6dc) ||
  456.         (c >= 0x6dd && c <= 0x6df) ||
  457.         (c >= 0x6e0 && c <= 0x6e4) ||
  458.         (c >= 0x6e7 && c <= 0x6e8) ||
  459.         (c >= 0x6ea && c <= 0x6ed) ||
  460.         (c >= 0x901 && c <= 0x903) ||
  461.         c == 0x93c ||
  462.         (c >= 0x93e && c <= 0x94c) ||
  463.         c == 0x94d ||
  464.         (c >= 0x951 && c <= 0x954) ||
  465.         (c >= 0x962 && c <= 0x963) ||
  466.         (c >= 0x981 && c <= 0x983) ||
  467.         c == 0x9bc ||
  468.         c == 0x9be ||
  469.         c == 0x9bf ||
  470.         (c >= 0x9c0 && c <= 0x9c4) ||
  471.         (c >= 0x9c7 && c <= 0x9c8) ||
  472.         (c >= 0x9cb && c <= 0x9cd) ||
  473.         c == 0x9d7 ||
  474.         (c >= 0x9e2 && c <= 0x9e3) ||
  475.         c == 0xa02 ||
  476.         c == 0xa3c ||
  477.         c == 0xa3e ||
  478.         c == 0xa3f ||
  479.         (c >= 0xa40 && c <= 0xa42) ||
  480.         (c >= 0xa47 && c <= 0xa48) ||
  481.         (c >= 0xa4b && c <= 0xa4d) ||
  482.         (c >= 0xa70 && c <= 0xa71) ||
  483.         (c >= 0xa81 && c <= 0xa83) ||
  484.         c == 0xabc ||
  485.         (c >= 0xabe && c <= 0xac5) ||
  486.         (c >= 0xac7 && c <= 0xac9) ||
  487.         (c >= 0xacb && c <= 0xacd) ||
  488.         (c >= 0xb01 && c <= 0xb03) ||
  489.         c == 0xb3c ||
  490.         (c >= 0xb3e && c <= 0xb43) ||
  491.         (c >= 0xb47 && c <= 0xb48) ||
  492.         (c >= 0xb4b && c <= 0xb4d) ||
  493.         (c >= 0xb56 && c <= 0xb57) ||
  494.         (c >= 0xb82 && c <= 0xb83) ||
  495.         (c >= 0xbbe && c <= 0xbc2) ||
  496.         (c >= 0xbc6 && c <= 0xbc8) ||
  497.         (c >= 0xbca && c <= 0xbcd) ||
  498.         c == 0xbd7 ||
  499.         (c >= 0xc01 && c <= 0xc03) ||
  500.         (c >= 0xc3e && c <= 0xc44) ||
  501.         (c >= 0xc46 && c <= 0xc48) ||
  502.         (c >= 0xc4a && c <= 0xc4d) ||
  503.         (c >= 0xc55 && c <= 0xc56) ||
  504.         (c >= 0xc82 && c <= 0xc83) ||
  505.         (c >= 0xcbe && c <= 0xcc4) ||
  506.         (c >= 0xcc6 && c <= 0xcc8) ||
  507.         (c >= 0xcca && c <= 0xccd) ||
  508.         (c >= 0xcd5 && c <= 0xcd6) ||
  509.         (c >= 0xd02 && c <= 0xd03) ||
  510.         (c >= 0xd3e && c <= 0xd43) ||
  511.         (c >= 0xd46 && c <= 0xd48) ||
  512.         (c >= 0xd4a && c <= 0xd4d) ||
  513.         c == 0xd57 ||
  514.         c == 0xe31 ||
  515.         (c >= 0xe34 && c <= 0xe3a) ||
  516.         (c >= 0xe47 && c <= 0xe4e) ||
  517.         c == 0xeb1 ||
  518.         (c >= 0xeb4 && c <= 0xeb9) ||
  519.         (c >= 0xebb && c <= 0xebc) ||
  520.         (c >= 0xec8 && c <= 0xecd) ||
  521.         (c >= 0xf18 && c <= 0xf19) ||
  522.         c == 0xf35 ||
  523.         c == 0xf37 ||
  524.         c == 0xf39 ||
  525.         c == 0xf3e ||
  526.         c == 0xf3f ||
  527.         (c >= 0xf71 && c <= 0xf84) ||
  528.         (c >= 0xf86 && c <= 0xf8b) ||
  529.         (c >= 0xf90 && c <= 0xf95) ||
  530.         c == 0xf97 ||
  531.         (c >= 0xf99 && c <= 0xfad) ||
  532.         (c >= 0xfb1 && c <= 0xfb7) ||
  533.         c == 0xfb9 ||
  534.         (c >= 0x20d0 && c <= 0x20dc) ||
  535.         c == 0x20e1 ||
  536.         (c >= 0x302a && c <= 0x302f) ||
  537.         c == 0x3099 ||
  538.         c == 0x309a ||
  539.         (c >= 0x30 && c <= 0x39) ||
  540.         (c >= 0x660 && c <= 0x669) ||
  541.         (c >= 0x6f0 && c <= 0x6f9) ||
  542.         (c >= 0x966 && c <= 0x96f) ||
  543.         (c >= 0x9e6 && c <= 0x9ef) ||
  544.         (c >= 0xa66 && c <= 0xa6f) ||
  545.         (c >= 0xae6 && c <= 0xaef) ||
  546.         (c >= 0xb66 && c <= 0xb6f) ||
  547.         (c >= 0xbe7 && c <= 0xbef) ||
  548.         (c >= 0xc66 && c <= 0xc6f) ||
  549.         (c >= 0xce6 && c <= 0xcef) ||
  550.         (c >= 0xd66 && c <= 0xd6f) ||
  551.         (c >= 0xe50 && c <= 0xe59) ||
  552.         (c >= 0xed0 && c <= 0xed9) ||
  553.         (c >= 0xf20 && c <= 0xf29) ||
  554.         c == 0xb7 ||
  555.         c == 0x2d0 ||
  556.         c == 0x2d1 ||
  557.         c == 0x387 ||
  558.         c == 0x640 ||
  559.         c == 0xe46 ||
  560.         c == 0xec6 ||
  561.         c == 0x3005 ||
  562.         (c >= 0x3031 && c <= 0x3035) ||
  563.         (c >= 0x309d && c <= 0x309e) ||
  564.         (c >= 0x30fc && c <= 0x30fe));
  565. }
  566.  
  567. Bool IsLower(uint c)
  568. {
  569.     uint map = MAP(c);
  570.  
  571.     return (Bool)(map & lowercase);
  572. }
  573.  
  574. Bool IsUpper(uint c)
  575. {
  576.     uint map = MAP(c);
  577.  
  578.     return (Bool)(map & uppercase);
  579. }
  580.  
  581. uint ToLower(uint c)
  582. {
  583.     uint map = MAP(c);
  584.  
  585.     if (map & uppercase)
  586.         c += 'a' - 'A';
  587.  
  588.     return c;
  589. }
  590.  
  591. uint ToUpper(uint c)
  592. {
  593.     uint map = MAP(c);
  594.  
  595.     if (map & lowercase)
  596.         c += (uint) ('A' - 'a' );
  597.  
  598.     return c;
  599. }
  600.  
  601. char FoldCase( TidyDocImpl* doc, tmbchar c, Bool tocaps )
  602. {
  603.     if ( !cfgBool(doc, TidyXmlTags) )
  604.     {
  605.         if ( tocaps )
  606.         {
  607.             c = (tmbchar) ToUpper(c);
  608.         }
  609.         else /* force to lower case */
  610.         {
  611.             c = (tmbchar) ToLower(c);
  612.         }
  613.     }
  614.     return c;
  615. }
  616.  
  617.  
  618. /*
  619.  return last character in string
  620.  this is useful when trailing quotemark
  621.  is missing on an attribute
  622. */
  623. static tmbchar LastChar( tmbstr str )
  624. {
  625.     if ( str && *str )
  626.     {
  627.         int n = tmbstrlen(str);
  628.         return str[n-1];
  629.     }
  630.     return 0;
  631. }
  632.  
  633. /*
  634.    node->type is one of these:
  635.  
  636.     #define TextNode    1
  637.     #define StartTag    2
  638.     #define EndTag      3
  639.     #define StartEndTag 4
  640. */
  641.  
  642. Lexer* NewLexer( TidyDocImpl* doc )
  643. {
  644.     Lexer* lexer = (Lexer*) MemAlloc( sizeof(Lexer) );
  645.  
  646.     if ( lexer != NULL )
  647.     {
  648.         ClearMemory( lexer, sizeof(Lexer) );
  649.  
  650.         lexer->lines = 1;
  651.         lexer->columns = 1;
  652.         lexer->state = LEX_CONTENT;
  653.  
  654.         lexer->versions = (VERS_ALL|VERS_PROPRIETARY);
  655.         lexer->doctype = VERS_UNKNOWN;
  656.         lexer->root = &doc->root;
  657.     }
  658.     return lexer;
  659. }
  660.  
  661. Bool EndOfInput( TidyDocImpl* doc )
  662. {
  663.     assert( doc->docIn != NULL );
  664.     return ( !doc->docIn->pushed && IsEOF(doc->docIn) );
  665. }
  666.  
  667. void FreeLexer( TidyDocImpl* doc )
  668. {
  669.     Lexer *lexer = doc->lexer;
  670.     if ( lexer )
  671.     {
  672.         FreeStyles( doc );
  673.  
  674.         if ( lexer->pushed )
  675.             FreeNode( doc, lexer->token );
  676.  
  677.         while ( lexer->istacksize > 0 )
  678.             PopInline( doc, NULL );
  679.  
  680.         MemFree( lexer->istack );
  681.         MemFree( lexer->lexbuf );
  682.         MemFree( lexer );
  683.         doc->lexer = NULL;
  684.     }
  685. }
  686.  
  687. /* Lexer uses bigger memory chunks than pprint as
  688. ** it must hold the entire input document. not just
  689. ** the last line or three.
  690. */
  691. void AddByte( Lexer *lexer, tmbchar ch )
  692. {
  693.     if ( lexer->lexsize + 1 >= lexer->lexlength )
  694.     {
  695.         tmbstr buf = NULL;
  696.         uint allocAmt = lexer->lexlength;
  697.         while ( lexer->lexsize + 1 >= allocAmt )
  698.         {
  699.             if ( allocAmt == 0 )
  700.                 allocAmt = 8192;
  701.             else
  702.                 allocAmt *= 2;
  703.         }
  704.         buf = (tmbstr) MemRealloc( lexer->lexbuf, allocAmt );
  705.         if ( buf )
  706.         {
  707.           ClearMemory( buf + lexer->lexlength, 
  708.                        allocAmt - lexer->lexlength );
  709.           lexer->lexbuf = buf;
  710.           lexer->lexlength = allocAmt;
  711.         }
  712.     }
  713.  
  714.     lexer->lexbuf[ lexer->lexsize++ ] = ch;
  715.     lexer->lexbuf[ lexer->lexsize ]   = '\0';  /* debug */
  716. }
  717.  
  718. static void ChangeChar( Lexer *lexer, tmbchar c )
  719. {
  720.     if ( lexer->lexsize > 0 )
  721.     {
  722.         lexer->lexbuf[ lexer->lexsize-1 ] = c;
  723.     }
  724. }
  725.  
  726. /* store character c as UTF-8 encoded byte stream */
  727. void AddCharToLexer( Lexer *lexer, uint c )
  728. {
  729.     int i, err, count = 0;
  730.     tmbchar buf[10] = {0};
  731.     
  732.     err = EncodeCharToUTF8Bytes( c, buf, NULL, &count );
  733.     if (err)
  734.     {
  735. #if 0 && defined(_DEBUG)
  736.         fprintf( stderr, "lexer UTF-8 encoding error for U+%x : ", c );
  737. #endif
  738.         /* replacement character 0xFFFD encoded as UTF-8 */
  739.         buf[0] = (byte) 0xEF;
  740.         buf[1] = (byte) 0xBF;
  741.         buf[2] = (byte) 0xBD;
  742.         count = 3;
  743.     }
  744.     
  745.     for ( i = 0; i < count; ++i )
  746.         AddByte( lexer, buf[i] );
  747. }
  748.  
  749. static void AddStringToLexer( Lexer *lexer, ctmbstr str )
  750. {
  751.     uint c;
  752.  
  753.     /*  Many (all?) compilers will sign-extend signed chars (the default) when
  754.     **  converting them to unsigned integer values.  We must cast our char to
  755.     **  unsigned char before assigning it to prevent this from happening.
  756.     */
  757.     while( 0 != (c = (unsigned char) *str++ ))
  758.         AddCharToLexer( lexer, c );
  759. }
  760.  
  761. /*
  762.   No longer attempts to insert missing ';' for unknown
  763.   enitities unless one was present already, since this
  764.   gives unexpected results.
  765.  
  766.   For example:   <a href="something.htm?foo&bar&fred">
  767.   was tidied to: <a href="something.htm?foo&bar;&fred;">
  768.   rather than:   <a href="something.htm?foo&bar&fred">
  769.  
  770.   My thanks for Maurice Buxton for spotting this.
  771.  
  772.   Also Randy Waki pointed out the following case for the
  773.   04 Aug 00 version (bug #433012):
  774.   
  775.   For example:   <a href="something.htm?id=1&lang=en">
  776.   was tidied to: <a href="something.htm?id=1⟨=en">
  777.   rather than:   <a href="something.htm?id=1&lang=en">
  778.   
  779.   where "lang" is a known entity (#9001), but browsers would
  780.   misinterpret "⟨" because it had a value > 256.
  781.   
  782.   So the case of an apparently known entity with a value > 256 and
  783.   missing a semicolon is handled specially.
  784.   
  785.   "ParseEntity" is also a bit of a misnomer - it handles entities and
  786.   numeric character references. Invalid NCR's are now reported.
  787. */
  788. static void ParseEntity( TidyDocImpl* doc, int mode )
  789. {
  790.     uint start;
  791.     Bool first = yes, semicolon = no, found = no;
  792.     Bool isXml = cfgBool( doc, TidyXmlTags );
  793.     uint c, ch, startcol, entver = 0;
  794.     Lexer* lexer = doc->lexer;
  795.  
  796.     start = lexer->lexsize - 1;  /* to start at "&" */
  797.     startcol = doc->docIn->curcol - 1;
  798.  
  799.     while ( (c = ReadChar(doc->docIn)) != EndOfStream )
  800.     {
  801.         if ( c == ';' )
  802.         {
  803.             semicolon = yes;
  804.             break;
  805.         }
  806.  
  807.         if (first && c == '#')
  808.         {
  809. #if SUPPORT_ASIAN_ENCODINGS
  810.             if ( !cfgBool(doc, TidyNCR) || 
  811.                  cfg(doc, TidyInCharEncoding) == BIG5 ||
  812.                  cfg(doc, TidyInCharEncoding) == SHIFTJIS )
  813.             {
  814.                 UngetChar('#', doc->docIn);
  815.                 return;
  816.             }
  817. #endif
  818.             AddCharToLexer( lexer, c );
  819.             first = no;
  820.             continue;
  821.         }
  822.  
  823.         first = no;
  824.  
  825.         if ( IsNamechar(c) )
  826.         {
  827.             AddCharToLexer( lexer, c );
  828.             continue;
  829.         }
  830.  
  831.         /* otherwise put it back */
  832.  
  833.         UngetChar( c, doc->docIn );
  834.         break;
  835.     }
  836.  
  837.     /* make sure entity is NULL terminated */
  838.     lexer->lexbuf[lexer->lexsize] = '\0';
  839.  
  840.     /* Should contrain version to XML/XHTML if ' 
  841.     ** is encountered.  But this is not possible with
  842.     ** Tidy's content model bit mask.
  843.     */
  844.     if ( tmbstrcmp(lexer->lexbuf+start, "&apos") == 0
  845.          && !cfgBool(doc, TidyXmlOut)
  846.          && !lexer->isvoyager
  847.          && !cfgBool(doc, TidyXhtmlOut) )
  848.         ReportEntityError( doc, APOS_UNDEFINED, lexer->lexbuf+start, 39 );
  849.  
  850.     /* Lookup entity code and version
  851.     */
  852.     found = EntityInfo( lexer->lexbuf+start, isXml, &ch, &entver );
  853.  
  854.     /* deal with unrecognized or invalid entities */
  855.     /* #433012 - fix by Randy Waki 17 Feb 01 */
  856.     /* report invalid NCR's - Terry Teague 01 Sep 01 */
  857.     if ( !found || (ch >= 128 && ch <= 159) || (ch >= 256 && c != ';') )
  858.     {
  859.         /* set error position just before offending character */
  860.         lexer->lines = doc->docIn->curline;
  861.         lexer->columns = startcol;
  862.  
  863.         if (lexer->lexsize > start + 1)
  864.         {
  865.             if (ch >= 128 && ch <= 159)
  866.             {
  867.                 /* invalid numeric character reference */
  868.                 
  869.                 int c1 = 0, replaceMode = DISCARDED_CHAR;
  870.             
  871.                 if ( ReplacementCharEncoding == WIN1252 )
  872.                     c1 = DecodeWin1252( ch );
  873.                 else if ( ReplacementCharEncoding == MACROMAN )
  874.                     c1 = DecodeMacRoman( ch );
  875.  
  876.                 if ( c1 )
  877.                     replaceMode = REPLACED_CHAR;
  878.                 
  879.                 if ( c != ';' )  /* issue warning if not terminated by ';' */
  880.                     ReportEntityError( doc, MISSING_SEMICOLON_NCR,
  881.                                        lexer->lexbuf+start, c );
  882.  
  883.                 ReportEncodingError(doc, INVALID_NCR, ch, replaceMode == DISCARDED_CHAR);
  884.                 
  885.                 if ( c1 )
  886.                 {
  887.                     /* make the replacement */
  888.                     lexer->lexsize = start;
  889.                     AddCharToLexer( lexer, c1 );
  890.                     semicolon = no;
  891.                 }
  892.                 else
  893.                 {
  894.                     /* discard */
  895.                     lexer->lexsize = start;
  896.                     semicolon = no;
  897.                }
  898.                
  899.             }
  900.             else
  901.                 ReportEntityError( doc, UNKNOWN_ENTITY,
  902.                                    lexer->lexbuf+start, ch );
  903.  
  904.             if (semicolon)
  905.                 AddCharToLexer( lexer, ';' );
  906.         }
  907.         else /* naked & */
  908.             ReportEntityError( doc, UNESCAPED_AMPERSAND,
  909.                                lexer->lexbuf+start, ch );
  910.     }
  911.     else
  912.     {
  913.         if ( c != ';' )    /* issue warning if not terminated by ';' */
  914.         {
  915.             /* set error position just before offending chararcter */
  916.             lexer->lines = doc->docIn->curline;
  917.             lexer->columns = startcol;
  918.             ReportEntityError( doc, MISSING_SEMICOLON, lexer->lexbuf+start, c );
  919.         }
  920.  
  921.         lexer->lexsize = start;
  922.         if ( ch == 160 && (mode & Preformatted) )
  923.             ch = ' ';
  924.         AddCharToLexer( lexer, ch );
  925.  
  926.         if ( ch == '&' && !cfgBool(doc, TidyQuoteAmpersand) )
  927.             AddStringToLexer( lexer, "amp;" );
  928.  
  929.         /* Detect extended vs. basic entities */
  930.         ConstrainVersion( doc, entver );
  931.     }
  932. }
  933.  
  934. static tmbchar ParseTagName( TidyDocImpl* doc )
  935. {
  936.     Lexer *lexer = doc->lexer;
  937.     uint c = lexer->lexbuf[ lexer->txtstart ];
  938.     Bool xml = cfgBool(doc, TidyXmlTags);
  939.  
  940.     /* fold case of first character in buffer */
  941.     if (!xml && IsUpper(c))
  942.         lexer->lexbuf[lexer->txtstart] = (tmbchar) ToLower(c);
  943.  
  944.     while ((c = ReadChar(doc->docIn)) != EndOfStream)
  945.     {
  946.         if ((!xml && !IsNamechar(c)) ||
  947.             (xml && !IsXMLNamechar(c)))
  948.             break;
  949.  
  950.         /* fold case of subsequent characters */
  951.         if (!xml && IsUpper(c))
  952.              c = ToLower(c);
  953.  
  954.         AddCharToLexer(lexer, c);
  955.     }
  956.  
  957.     lexer->txtend = lexer->lexsize;
  958.     return (tmbchar) c;
  959. }
  960.  
  961. /*
  962.   Used for elements and text nodes
  963.   element name is NULL for text nodes
  964.   start and end are offsets into lexbuf
  965.   which contains the textual content of
  966.   all elements in the parse tree.
  967.  
  968.   parent and content allow traversal
  969.   of the parse tree in any direction.
  970.   attributes are represented as a linked
  971.   list of AttVal nodes which hold the
  972.   strings for attribute/value pairs.
  973. */
  974.  
  975.  
  976. Node *NewNode(Lexer *lexer)
  977. {
  978.     Node* node = (Node*) MemAlloc( sizeof(Node) );
  979.     ClearMemory( node, sizeof(Node) );
  980.     if ( lexer )
  981.     {
  982.         node->line = lexer->lines;
  983.         node->column = lexer->columns;
  984.     }
  985.     node->type = TextNode;
  986.     return node;
  987. }
  988.  
  989. /* used to clone heading nodes when split by an <HR> */
  990. Node *CloneNode( TidyDocImpl* doc, Node *element )
  991. {
  992.     Lexer* lexer = doc->lexer;
  993.     Node *node = NewNode( lexer );
  994.  
  995.     node->start = lexer->lexsize;
  996.     node->end   = lexer->lexsize;
  997.  
  998.     if ( element )
  999.     {
  1000.         node->parent     = element->parent;
  1001.         node->type       = element->type;
  1002.         node->closed     = element->closed;
  1003.         node->implicit   = element->implicit;
  1004.         node->tag        = element->tag;
  1005.         node->element    = tmbstrdup( element->element );
  1006.         node->attributes = DupAttrs( doc, element->attributes );
  1007.     }
  1008.     return node;
  1009. }
  1010.  
  1011. /* free node's attributes */
  1012. void FreeAttrs( TidyDocImpl* doc, Node *node )
  1013. {
  1014.  
  1015.     while ( node->attributes )
  1016.     {
  1017.         AttVal *av = node->attributes;
  1018.  
  1019.         if ( av->attribute )
  1020.         {
  1021.             if ( (attrIsID(av) || attrIsNAME(av)) &&
  1022.                  IsAnchorElement(doc, node) )
  1023.             {
  1024.                 RemoveAnchorByNode( doc, node );
  1025.             }
  1026.         }
  1027.  
  1028.         node->attributes = av->next;
  1029.         FreeAttribute( doc, av );
  1030.     }
  1031. }
  1032.  
  1033. /* doesn't repair attribute list linkage */
  1034. void FreeAttribute( TidyDocImpl* doc, AttVal *av )
  1035. {
  1036.     FreeNode( doc, av->asp );
  1037.     FreeNode( doc, av->php );
  1038.     MemFree( av->attribute );
  1039.     MemFree( av->value );
  1040.     MemFree( av );
  1041. }
  1042.  
  1043. /* remove attribute from node then free it
  1044. */
  1045. void RemoveAttribute( TidyDocImpl* doc, Node *node, AttVal *attr )
  1046. {
  1047.     AttVal *av, *prev = NULL;
  1048.  
  1049.     for ( av = node->attributes; av; av = av->next )
  1050.     {
  1051.         if ( av == attr )
  1052.         {
  1053.             if ( prev )
  1054.                 prev->next = attr->next;
  1055.             else
  1056.                 node->attributes = attr->next;
  1057.             break;
  1058.         }
  1059.         prev = av;
  1060.     }
  1061.     FreeAttribute( doc, attr );
  1062. }
  1063.  
  1064. /*
  1065.   Free document nodes by iterating through peers and recursing
  1066.   through children. Set next to NULL before calling FreeNode()
  1067.   to avoid freeing peer nodes. Doesn't patch up prev/next links.
  1068.  */
  1069. void FreeNode( TidyDocImpl* doc, Node *node )
  1070. {
  1071.     while ( node )
  1072.     {
  1073.         Node* next = node->next;
  1074.  
  1075.         FreeAttrs( doc, node );
  1076.         FreeNode( doc, node->content );
  1077.         MemFree( node->element );
  1078. #ifdef TIDY_STORE_ORIGINAL_TEXT
  1079.         if (node->otext)
  1080.             MemFree(node->otext);
  1081. #endif
  1082.         if (RootNode != node->type)
  1083.             MemFree( node );
  1084.         else
  1085.             node->content = NULL;
  1086.  
  1087.         node = next;
  1088.     }
  1089. }
  1090.  
  1091. #ifdef TIDY_STORE_ORIGINAL_TEXT
  1092. void StoreOriginalTextInToken(TidyDocImpl* doc, Node* node, uint count)
  1093. {
  1094.     if (!doc->storeText)
  1095.         return;
  1096.  
  1097.     if (count >= doc->docIn->otextlen)
  1098.         return;
  1099.  
  1100.     if (!doc->docIn->otextsize)
  1101.         return;
  1102.  
  1103.     if (count == 0)
  1104.     {
  1105.         node->otext = doc->docIn->otextbuf;
  1106.         doc->docIn->otextbuf = NULL;
  1107.         doc->docIn->otextlen = 0;
  1108.         doc->docIn->otextsize = 0;
  1109.     }
  1110.     else
  1111.     {
  1112.         uint len = doc->docIn->otextlen;
  1113.         tmbstr buf1 = (tmbstr)MemAlloc(len - count + 1);
  1114.         tmbstr buf2 = (tmbstr)MemAlloc(count + 1);
  1115.         uint i, j;
  1116.  
  1117.         /* strncpy? */
  1118.  
  1119.         for (i = 0; i < len - count; ++i)
  1120.             buf1[i] = doc->docIn->otextbuf[i];
  1121.  
  1122.         buf1[i] = 0;
  1123.  
  1124.         for (j = 0; j + i < len; ++j)
  1125.             buf2[j] = doc->docIn->otextbuf[j + i];
  1126.  
  1127.         buf2[j] = 0;
  1128.  
  1129.         MemFree(doc->docIn->otextbuf);
  1130.         node->otext = buf1;
  1131.         doc->docIn->otextbuf = buf2;
  1132.         doc->docIn->otextlen = count;
  1133.         doc->docIn->otextsize = count + 1;
  1134.     }
  1135. }
  1136. #endif
  1137.  
  1138. Node* TextToken( Lexer *lexer )
  1139. {
  1140.     Node *node = NewNode( lexer );
  1141.     node->start = lexer->txtstart;
  1142.     node->end = lexer->txtend;
  1143.     return node;
  1144. }
  1145.  
  1146. /* used for creating preformatted text from Word2000 */
  1147. Node *NewLineNode( Lexer *lexer )
  1148. {
  1149.     Node *node = NewNode( lexer );
  1150.     node->start = lexer->lexsize;
  1151.     AddCharToLexer( lexer, (uint)'\n' );
  1152.     node->end = lexer->lexsize;
  1153.     return node;
  1154. }
  1155.  
  1156. /* used for adding a   for Word2000 */
  1157. Node* NewLiteralTextNode( Lexer *lexer, ctmbstr txt )
  1158. {
  1159.     Node *node = NewNode( lexer );
  1160.     node->start = lexer->lexsize;
  1161.     AddStringToLexer( lexer, txt );
  1162.     node->end = lexer->lexsize;
  1163.     return node;
  1164. }
  1165.  
  1166. static Node* TagToken( TidyDocImpl* doc, uint type )
  1167. {
  1168.     Lexer* lexer = doc->lexer;
  1169.     Node* node = NewNode( lexer );
  1170.     node->type = type;
  1171.     node->element = tmbstrndup( lexer->lexbuf + lexer->txtstart,
  1172.                                 lexer->txtend - lexer->txtstart );
  1173.     node->start = lexer->txtstart;
  1174.     node->end = lexer->txtstart;
  1175.  
  1176.     if ( type == StartTag || type == StartEndTag || type == EndTag )
  1177.         FindTag(doc, node);
  1178.  
  1179.     return node;
  1180. }
  1181.  
  1182. static Node* NewToken(TidyDocImpl* doc, uint type)
  1183. {
  1184.     Lexer* lexer = doc->lexer;
  1185.     Node* node = NewNode(lexer);
  1186.     node->type = type;
  1187.     node->start = lexer->txtstart;
  1188.     node->end = lexer->txtend;
  1189. #ifdef TIDY_STORE_ORIGINAL_TEXT
  1190.     StoreOriginalTextInToken(doc, node, 0);
  1191. #endif
  1192.     return node;
  1193. }
  1194.  
  1195. #define CommentToken(doc) NewToken(doc, CommentTag)
  1196. #define DocTypeToken(doc) NewToken(doc, DocTypeTag)
  1197. #define PIToken(doc)      NewToken(doc, ProcInsTag)
  1198. #define AspToken(doc)     NewToken(doc, AspTag)
  1199. #define JsteToken(doc)    NewToken(doc, JsteTag)
  1200. #define PhpToken(doc)     NewToken(doc, PhpTag)
  1201. #define XmlDeclToken(doc) NewToken(doc, XmlDecl)
  1202. #define SectionToken(doc) NewToken(doc, SectionTag)
  1203. #define CDATAToken(doc)   NewToken(doc, CDATATag)
  1204.  
  1205. void AddStringLiteral( Lexer* lexer, ctmbstr str )
  1206. {
  1207.     byte c;
  1208.     while(0 != (c = *str++) )
  1209.         AddCharToLexer( lexer, c );
  1210. }
  1211.  
  1212. void AddStringLiteralLen( Lexer* lexer, ctmbstr str, int len )
  1213. {
  1214.     byte c;
  1215.     int ix;
  1216.  
  1217.     for ( ix=0; ix < len && (c = *str++); ++ix )
  1218.         AddCharToLexer(lexer, c);
  1219. }
  1220.  
  1221. /* find doctype element */
  1222. Node *FindDocType( TidyDocImpl* doc )
  1223. {
  1224.     Node* node;
  1225.     for ( node = (doc ? doc->root.content : NULL);
  1226.           node && node->type != DocTypeTag; 
  1227.           node = node->next )
  1228.         /**/;
  1229.     return node;
  1230. }
  1231.  
  1232. /* find parent container element */
  1233. Node* FindContainer( Node* node )
  1234. {
  1235.     for ( node = (node ? node->parent : NULL);
  1236.           node && nodeHasCM(node, CM_INLINE);
  1237.           node = node->parent )
  1238.         /**/;
  1239.  
  1240.     return node;
  1241. }
  1242.  
  1243.  
  1244. /* find html element */
  1245. Node *FindHTML( TidyDocImpl* doc )
  1246. {
  1247.     Node *node;
  1248.     for ( node = (doc ? doc->root.content : NULL);
  1249.           node && !nodeIsHTML(node); 
  1250.           node = node->next )
  1251.         /**/;
  1252.  
  1253.     return node;
  1254. }
  1255.  
  1256. /* find XML Declaration */
  1257. Node *FindXmlDecl(TidyDocImpl* doc)
  1258. {
  1259.     Node *node;
  1260.     for ( node = (doc ? doc->root.content : NULL);
  1261.           node && !(node->type == XmlDecl);
  1262.           node = node->next )
  1263.         /**/;
  1264.  
  1265.     return node;
  1266. }
  1267.  
  1268.  
  1269. Node *FindHEAD( TidyDocImpl* doc )
  1270. {
  1271.     Node *node = FindHTML( doc );
  1272.  
  1273.     if ( node )
  1274.     {
  1275.         for ( node = node->content;
  1276.               node && !nodeIsHEAD(node); 
  1277.               node = node->next )
  1278.             /**/;
  1279.     }
  1280.  
  1281.     return node;
  1282. }
  1283.  
  1284. Node *FindTITLE(TidyDocImpl* doc)
  1285. {
  1286.     Node *node = FindHEAD(doc);
  1287.  
  1288.     if (node)
  1289.         for (node = node->content;
  1290.              node && !nodeIsTITLE(node);
  1291.              node = node->next) {}
  1292.  
  1293.     return node;
  1294. }
  1295.  
  1296. Node *FindBody( TidyDocImpl* doc )
  1297. {
  1298.     Node *node = ( doc ? doc->root.content : NULL );
  1299.  
  1300.     while ( node && !nodeIsHTML(node) )
  1301.         node = node->next;
  1302.  
  1303.     if (node == NULL)
  1304.         return NULL;
  1305.  
  1306.     node = node->content;
  1307.     while ( node && !nodeIsBODY(node) && !nodeIsFRAMESET(node) )
  1308.         node = node->next;
  1309.  
  1310.     if ( node && nodeIsFRAMESET(node) )
  1311.     {
  1312.         node = node->content;
  1313.         while ( node && !nodeIsNOFRAMES(node) )
  1314.             node = node->next;
  1315.  
  1316.         if ( node )
  1317.         {
  1318.             node = node->content;
  1319.             while ( node && !nodeIsBODY(node) )
  1320.                 node = node->next;
  1321.         }
  1322.     }
  1323.  
  1324.     return node;
  1325. }
  1326.  
  1327. /* add meta element for Tidy */
  1328. Bool AddGenerator( TidyDocImpl* doc )
  1329. {
  1330.     AttVal *attval;
  1331.     Node *node;
  1332.     Node *head = FindHEAD( doc );
  1333.     tmbchar buf[256];
  1334.     
  1335.     if (head)
  1336.     {
  1337. #ifdef PLATFORM_NAME
  1338.         tmbsnprintf(buf, sizeof(buf), "HTML Tidy for "PLATFORM_NAME" (vers %s), see www.w3.org",
  1339.                  tidyReleaseDate());
  1340. #else
  1341.         tmbsnprintf(buf, sizeof(buf), "HTML Tidy (vers %s), see www.w3.org", tidyReleaseDate());
  1342. #endif
  1343.  
  1344.         for ( node = head->content; node; node = node->next )
  1345.         {
  1346.             if ( nodeIsMETA(node) )
  1347.             {
  1348.                 attval = AttrGetById(node, TidyAttr_NAME);
  1349.  
  1350.                 if (AttrValueIs(attval, "generator"))
  1351.                 {
  1352.                     attval = AttrGetById(node, TidyAttr_CONTENT);
  1353.  
  1354.                     if (AttrHasValue(attval) &&
  1355.                         tmbstrncasecmp(attval->value, "HTML Tidy", 9) == 0)
  1356.                     {
  1357.                         /* update the existing content to reflect the */
  1358.                         /* actual version of Tidy currently being used */
  1359.                         
  1360.                         MemFree(attval->value);
  1361.                         attval->value = tmbstrdup(buf);
  1362.                         return no;
  1363.                     }
  1364.                 }
  1365.             }
  1366.         }
  1367.  
  1368.         if ( cfg(doc, TidyAccessibilityCheckLevel) == 0 )
  1369.         {
  1370.             node = InferredTag(doc, TidyTag_META);
  1371.             AddAttribute( doc, node, "name", "generator" );
  1372.             AddAttribute( doc, node, "content", buf );
  1373.             InsertNodeAtStart( head, node );
  1374.             return yes;
  1375.         }
  1376.     }
  1377.  
  1378.     return no;
  1379. }
  1380.  
  1381. /* examine <!DOCTYPE> to identify version */
  1382. int FindGivenVersion( TidyDocImpl* doc, Node* doctype )
  1383. {
  1384.     AttVal * fpi = GetAttrByName(doctype, "PUBLIC");
  1385.     uint vers;
  1386.  
  1387.     if (!fpi || !fpi->value)
  1388.         return 0;
  1389.  
  1390.     vers = GetVersFromFPI(fpi->value);
  1391.  
  1392.     if (VERS_XHTML & vers)
  1393.     {
  1394.         SetOptionBool(doc, TidyXmlOut, yes);
  1395.         SetOptionBool(doc, TidyXhtmlOut, yes);
  1396.         doc->lexer->isvoyager = yes;
  1397.     }
  1398.  
  1399.     /* todo: add a warning if case does not match? */
  1400.     MemFree(fpi->value);
  1401.     fpi->value = tmbstrdup(GetFPIFromVers(vers));
  1402.  
  1403.     return vers;
  1404. }
  1405.  
  1406. ctmbstr HTMLVersionNameFromCode( uint vers, Bool isXhtml )
  1407. {
  1408. #pragma unused(isXhtml)
  1409.  
  1410.     ctmbstr name = GetNameFromVers(vers);
  1411.  
  1412.     if (!name)
  1413.         name = "HTML Proprietary";
  1414.  
  1415.     return name;
  1416. }
  1417.  
  1418. /* Put DOCTYPE declaration between the
  1419. ** <?xml version "1.0" ... ?> declaration, if any,
  1420. ** and the <html> tag.  Should also work for any comments, 
  1421. ** etc. that may precede the <html> tag.
  1422. */
  1423.  
  1424. static Node* NewDocTypeNode( TidyDocImpl* doc )
  1425. {
  1426.     Node* doctype = NULL;
  1427.     Node* html = FindHTML( doc );
  1428.     Node* root = &doc->root;
  1429.     if ( !html )
  1430.         return NULL;
  1431.  
  1432.     doctype = NewNode( NULL );
  1433.     doctype->type = DocTypeTag;
  1434.     doctype->next = html;
  1435.     doctype->parent = root;
  1436.  
  1437.     if ( html == root->content )
  1438.     {
  1439.         /* No <?xml ... ?> declaration. */
  1440.         root->content->prev = doctype;
  1441.         root->content = doctype;
  1442.         doctype->prev = NULL;
  1443.     }
  1444.     else
  1445.     {
  1446.         /* we have an <?xml ... ?> declaration. */
  1447.         doctype->prev = html->prev;
  1448.         doctype->prev->next = doctype;
  1449.     }
  1450.     html->prev = doctype;
  1451.     return doctype;
  1452. }
  1453.  
  1454. Bool SetXHTMLDocType( TidyDocImpl* doc )
  1455. {
  1456.     Lexer *lexer = doc->lexer;
  1457.     Node *doctype = FindDocType( doc );
  1458.     uint dtmode = cfg(doc, TidyDoctypeMode);
  1459.     ctmbstr pub = "PUBLIC";
  1460.     ctmbstr sys = "SYSTEM";
  1461.  
  1462.     if (dtmode == TidyDoctypeOmit)
  1463.     {
  1464.         if (doctype)
  1465.             DiscardElement(doc, doctype);
  1466.         return yes;
  1467.     }
  1468.  
  1469.     if (dtmode == TidyDoctypeUser && !cfgStr(doc, TidyDoctype))
  1470.         return no;
  1471.  
  1472.     if (!doctype)
  1473.     {
  1474.         doctype = NewDocTypeNode(doc);
  1475.         doctype->element = tmbstrdup("html");
  1476.     }
  1477.     else
  1478.     {
  1479.         doctype->element = tmbstrtolower(doctype->element);
  1480.     }
  1481.  
  1482.     switch(dtmode)
  1483.     {
  1484.     case TidyDoctypeStrict:
  1485.         /* XHTML 1.0 Strict */
  1486.         RepairAttrValue(doc, doctype, pub, GetFPIFromVers(X10S));
  1487.         RepairAttrValue(doc, doctype, sys, GetSIFromVers(X10S));
  1488.         break;
  1489.     case TidyDoctypeLoose:
  1490.         /* XHTML 1.0 Transitional */
  1491.         RepairAttrValue(doc, doctype, pub, GetFPIFromVers(X10T));
  1492.         RepairAttrValue(doc, doctype, sys, GetSIFromVers(X10T));
  1493.         break;
  1494.     case TidyDoctypeUser:
  1495.         /* user defined document type declaration */
  1496.         RepairAttrValue(doc, doctype, pub, cfgStr(doc, TidyDoctype));
  1497.         RepairAttrValue(doc, doctype, sys, "");
  1498.         break;
  1499.     case TidyDoctypeAuto:
  1500.         if (lexer->versions & XH11 && lexer->doctype == XH11)
  1501.         {
  1502.             if (!GetAttrByName(doctype, sys))
  1503.                 RepairAttrValue(doc, doctype, sys, GetSIFromVers(XH11));
  1504.             return yes;
  1505.         }
  1506.         else if (lexer->versions & XH11 && !(lexer->versions & VERS_HTML40))
  1507.         {
  1508.             RepairAttrValue(doc, doctype, pub, GetFPIFromVers(XH11));
  1509.             RepairAttrValue(doc, doctype, sys, GetSIFromVers(XH11));
  1510.         }
  1511.         else if (lexer->versions & XB10 && lexer->doctype == XB10)
  1512.         {
  1513.             if (!GetAttrByName(doctype, sys))
  1514.                 RepairAttrValue(doc, doctype, sys, GetSIFromVers(XB10));
  1515.             return yes;
  1516.         }
  1517.         else if (lexer->versions & VERS_HTML40_STRICT)
  1518.         {
  1519.             RepairAttrValue(doc, doctype, pub, GetFPIFromVers(X10S));
  1520.             RepairAttrValue(doc, doctype, sys, GetSIFromVers(X10S));
  1521.         }
  1522.         else if (lexer->versions & VERS_FRAMESET)
  1523.         {
  1524.             RepairAttrValue(doc, doctype, pub, GetFPIFromVers(X10F));
  1525.             RepairAttrValue(doc, doctype, sys, GetSIFromVers(X10F));
  1526.         }
  1527.         else if (lexer->versions & VERS_LOOSE)
  1528.         {
  1529.             RepairAttrValue(doc, doctype, pub, GetFPIFromVers(X10T));
  1530.             RepairAttrValue(doc, doctype, sys, GetSIFromVers(X10T));
  1531.         }
  1532.         else
  1533.         {
  1534.             if (doctype)
  1535.                 DiscardElement(doc, doctype);
  1536.             return no;
  1537.         }
  1538.         break;
  1539.     }
  1540.  
  1541.     return no;
  1542. }
  1543.  
  1544. /* fixup doctype if missing */
  1545. Bool FixDocType( TidyDocImpl* doc )
  1546. {
  1547.     Lexer* lexer = doc->lexer;
  1548.     Node* doctype = FindDocType( doc );
  1549.     uint dtmode = cfg( doc, TidyDoctypeMode );
  1550.     uint guessed = VERS_UNKNOWN;
  1551.     Bool hadSI = no;
  1552.  
  1553.     if (dtmode == TidyDoctypeAuto &&
  1554.         lexer->versions & lexer->doctype &&
  1555.         !(VERS_XHTML & lexer->doctype && !lexer->isvoyager)
  1556.         && FindDocType(doc))
  1557.         return yes;
  1558.  
  1559.     if (dtmode == TidyDoctypeOmit)
  1560.     {
  1561.         if (doctype)
  1562.             DiscardElement( doc, doctype );
  1563.         return yes;
  1564.     }
  1565.  
  1566.     if (cfgBool(doc, TidyXmlOut))
  1567.         return yes;
  1568.  
  1569.     if (doctype)
  1570.         hadSI = GetAttrByName(doctype, "SYSTEM") != NULL;
  1571.  
  1572.     if ((dtmode == TidyDoctypeStrict ||
  1573.          dtmode == TidyDoctypeLoose) && doctype)
  1574.     {
  1575.             DiscardElement(doc, doctype);
  1576.             doctype = NULL;
  1577.     }
  1578.  
  1579.     switch (dtmode)
  1580.     {
  1581.     case TidyDoctypeStrict:
  1582.         guessed = H41S;
  1583.         break;
  1584.     case TidyDoctypeLoose:
  1585.         guessed = H41T;
  1586.         break;
  1587.     case TidyDoctypeAuto:
  1588.         guessed = HTMLVersion(doc);
  1589.         break;
  1590.     }
  1591.  
  1592.     if (guessed == VERS_UNKNOWN)
  1593.         return no;
  1594.  
  1595.     if (doctype)
  1596.     {
  1597.         doctype->element = tmbstrtolower(doctype->element);
  1598.     }
  1599.     else
  1600.     {
  1601.         doctype = NewDocTypeNode(doc);
  1602.         doctype->element = tmbstrdup("html");
  1603.     }
  1604.  
  1605.     RepairAttrValue(doc, doctype, "PUBLIC", GetFPIFromVers(guessed));
  1606.  
  1607.     if (hadSI)
  1608.         RepairAttrValue(doc, doctype, "SYSTEM", GetSIFromVers(guessed));
  1609.  
  1610.     return yes;
  1611. }
  1612.  
  1613. /* ensure XML document starts with <?XML version="1.0"?> */
  1614. /* add encoding attribute if not using ASCII or UTF-8 output */
  1615. Bool FixXmlDecl( TidyDocImpl* doc )
  1616. {
  1617.     Node* xml;
  1618.     AttVal *version, *encoding;
  1619.     Lexer*lexer = doc->lexer;
  1620.     Node* root = &doc->root;
  1621.  
  1622.     if ( root->content && root->content->type == XmlDecl )
  1623.     {
  1624.         xml = root->content;
  1625.     }
  1626.     else
  1627.     {
  1628.         xml = NewNode(lexer);
  1629.         xml->type = XmlDecl;
  1630.         xml->next = root->content;
  1631.         
  1632.         if ( root->content )
  1633.         {
  1634.             root->content->prev = xml;
  1635.             xml->next = root->content;
  1636.         }
  1637.         
  1638.         root->content = xml;
  1639.     }
  1640.  
  1641.     version = GetAttrByName(xml, "version");
  1642.     encoding = GetAttrByName(xml, "encoding");
  1643.  
  1644.     /*
  1645.       We need to insert a check if declared encoding 
  1646.       and output encoding mismatch and fix the XML
  1647.       declaration accordingly!!!
  1648.     */
  1649.  
  1650.     if ( encoding == NULL && cfg(doc, TidyOutCharEncoding) != UTF8 )
  1651.     {
  1652.         ctmbstr enc = GetEncodingNameFromTidyId(cfg(doc, TidyOutCharEncoding));
  1653.         if ( enc )
  1654.             AddAttribute( doc, xml, "encoding", enc );
  1655.     }
  1656.  
  1657.     if ( version == NULL )
  1658.         AddAttribute( doc, xml, "version", "1.0" );
  1659.     return yes;
  1660. }
  1661.  
  1662. Node* InferredTag(TidyDocImpl* doc, TidyTagId id)
  1663. {
  1664.     Lexer *lexer = doc->lexer;
  1665.     Node *node = NewNode( lexer );
  1666.     const Dict* dict = LookupTagDef(id);
  1667.  
  1668.     assert( dict != NULL );
  1669.  
  1670.     node->type = StartTag;
  1671.     node->implicit = yes;
  1672.     node->element = tmbstrdup(dict->name);
  1673.     node->tag = dict;
  1674.     node->start = lexer->txtstart;
  1675.     node->end = lexer->txtend;
  1676.  
  1677.     return node;
  1678. }
  1679.  
  1680. Bool ExpectsContent(Node *node)
  1681. {
  1682.     if (node->type != StartTag)
  1683.         return no;
  1684.  
  1685.     /* unknown element? */
  1686.     if (node->tag == NULL)
  1687.         return yes;
  1688.  
  1689.     if (node->tag->model & CM_EMPTY)
  1690.         return no;
  1691.  
  1692.     return yes;
  1693. }
  1694.  
  1695. /*
  1696.   create a text node for the contents of
  1697.   a CDATA element like style or script
  1698.   which ends with </foo> for some foo.
  1699. */
  1700.  
  1701. #define CDATA_INTERMEDIATE 1
  1702. #define CDATA_STARTTAG     2
  1703. #define CDATA_ENDTAG       3
  1704.  
  1705. Node *GetCDATA( TidyDocImpl* doc, Node *container )
  1706. {
  1707.     Lexer* lexer = doc->lexer;
  1708.     uint start = 0;
  1709.     int nested = 0;
  1710.     int state = CDATA_INTERMEDIATE;
  1711.     uint i;
  1712.     Bool isEmpty = yes;
  1713.     Bool matches = no;
  1714.     uint c;
  1715.     Bool hasSrc = AttrGetById(container, TidyAttr_SRC) != NULL;
  1716.  
  1717.     lexer->lines = doc->docIn->curline;
  1718.     lexer->columns = doc->docIn->curcol;
  1719.     lexer->waswhite = no;
  1720.     lexer->txtstart = lexer->txtend = lexer->lexsize;
  1721.  
  1722.     /* seen start tag, look for matching end tag */
  1723.     while ((c = ReadChar(doc->docIn)) != EndOfStream)
  1724.     {
  1725.         AddCharToLexer(lexer, (uint)c);
  1726.         lexer->txtend = lexer->lexsize;
  1727.  
  1728.         if (state == CDATA_INTERMEDIATE)
  1729.         {
  1730.             if (c != '<')
  1731.             {
  1732.                 if (isEmpty && !IsWhite(c))
  1733.                     isEmpty = no;
  1734.                 continue;
  1735.             }
  1736.  
  1737.             c = ReadChar(doc->docIn);
  1738.  
  1739.             if (IsLetter(c))
  1740.             {
  1741.                 /* <head><script src=foo><meta name=foo content=bar>*/
  1742.                 if (hasSrc && isEmpty && nodeIsSCRIPT(container))
  1743.                 {
  1744.                     /* ReportError(doc, container, NULL, MISSING_ENDTAG_FOR); */
  1745.                     lexer->lexsize = lexer->txtstart;
  1746.                     UngetChar(c, doc->docIn);
  1747.                     UngetChar('<', doc->docIn);
  1748.                     return NULL;
  1749.                 }
  1750.                 AddCharToLexer(lexer, (uint)c);
  1751.                 start = lexer->lexsize - 1;
  1752.                 state = CDATA_STARTTAG;
  1753.             }
  1754.             else if (c == '/')
  1755.             {
  1756.                 AddCharToLexer(lexer, (uint)c);
  1757.  
  1758.                 c = ReadChar(doc->docIn);
  1759.                 
  1760.                 if (!IsLetter(c))
  1761.                 {
  1762.                     UngetChar(c, doc->docIn);
  1763.                     continue;
  1764.                 }
  1765.                 UngetChar(c, doc->docIn);
  1766.  
  1767.                 start = lexer->lexsize;
  1768.                 state = CDATA_ENDTAG;
  1769.             }
  1770.             else if (c == '\\')
  1771.             {
  1772.                 /* recognize document.write("<script><\/script>") */
  1773.                 AddCharToLexer(lexer, (uint)c);
  1774.  
  1775.                 c = ReadChar(doc->docIn);
  1776.  
  1777.                 if (c != '/')
  1778.                 {
  1779.                     UngetChar(c, doc->docIn);
  1780.                     continue;
  1781.                 }
  1782.  
  1783.                 AddCharToLexer(lexer, (uint)c);
  1784.                 c = ReadChar(doc->docIn);
  1785.                 
  1786.                 if (!IsLetter(c))
  1787.                 {
  1788.                     UngetChar(c, doc->docIn);
  1789.                     continue;
  1790.                 }
  1791.                 UngetChar(c, doc->docIn);
  1792.  
  1793.                 start = lexer->lexsize;
  1794.                 state = CDATA_ENDTAG;
  1795.             }
  1796.             else
  1797.             {
  1798.                 UngetChar(c, doc->docIn);
  1799.             }
  1800.         }
  1801.         /* '<' + Letter found */
  1802.         else if (state == CDATA_STARTTAG)
  1803.         {
  1804.             if (IsLetter(c))
  1805.                 continue;
  1806.  
  1807.             matches = tmbstrncasecmp(container->element, lexer->lexbuf + start,
  1808.                                      tmbstrlen(container->element)) == 0;
  1809.             if (matches)
  1810.                 nested++;
  1811.  
  1812.             state = CDATA_INTERMEDIATE;
  1813.         }
  1814.         /* '<' + '/' + Letter found */
  1815.         else if (state == CDATA_ENDTAG)
  1816.         {
  1817.             if (IsLetter(c))
  1818.                 continue;
  1819.  
  1820.             matches = tmbstrncasecmp(container->element, lexer->lexbuf + start,
  1821.                                      tmbstrlen(container->element)) == 0;
  1822.  
  1823.             if (isEmpty && !matches)
  1824.             {
  1825.                 /* ReportError(doc, container, NULL, MISSING_ENDTAG_FOR); */
  1826.  
  1827.                 for (i = lexer->lexsize - 1; i >= start; --i)
  1828.                     UngetChar((uint)lexer->lexbuf[i], doc->docIn);
  1829.                 UngetChar('/', doc->docIn);
  1830.                 UngetChar('<', doc->docIn);
  1831.                 break;
  1832.             }
  1833.  
  1834.             if (matches && nested-- <= 0)
  1835.             {
  1836.                 for (i = lexer->lexsize - 1; i >= start; --i)
  1837.                     UngetChar((uint)lexer->lexbuf[i], doc->docIn);
  1838.                 UngetChar('/', doc->docIn);
  1839.                 UngetChar('<', doc->docIn);
  1840.                 lexer->lexsize -= (lexer->lexsize - start) + 2;
  1841.                 break;
  1842.             }
  1843.             else if (lexer->lexbuf[start - 2] != '\\')
  1844.             {
  1845.                 /* if the end tag is not already escaped using backslash */
  1846.                 lexer->lines = doc->docIn->curline;
  1847.                 lexer->columns = doc->docIn->curcol - 3;
  1848.                 ReportError(doc, NULL, NULL, BAD_CDATA_CONTENT);
  1849.  
  1850.                 /* if javascript insert backslash before / */
  1851.                 if (IsJavaScript(container))
  1852.                 {
  1853.                     for (i = lexer->lexsize; i > start-1; --i)
  1854.                         lexer->lexbuf[i] = lexer->lexbuf[i-1];
  1855.  
  1856.                     lexer->lexbuf[start-1] = '\\';
  1857.                     lexer->lexsize++;
  1858.                 }
  1859.             }
  1860.             state = CDATA_INTERMEDIATE;
  1861.         }
  1862.     }
  1863.     if (isEmpty)
  1864.         lexer->lexsize = lexer->txtstart = lexer->txtend;
  1865.     else
  1866.         lexer->txtend = lexer->lexsize;
  1867.  
  1868.     if (c == EndOfStream)
  1869.         ReportError(doc, container, NULL, MISSING_ENDTAG_FOR );
  1870.  
  1871.     /* if (lexer->txtend > lexer->txtstart) */
  1872.         return TextToken(lexer);
  1873.  
  1874.     return NULL;
  1875. }
  1876.  
  1877. void UngetToken( TidyDocImpl* doc )
  1878. {
  1879.     doc->lexer->pushed = yes;
  1880. }
  1881.  
  1882. #ifdef TIDY_STORE_ORIGINAL_TEXT
  1883. #define CondReturnTextNode(doc, skip) \
  1884.             if (lexer->txtend > lexer->txtstart) \
  1885.             { \
  1886.                 lexer->token = TextToken(lexer); \
  1887.                 StoreOriginalTextInToken(doc, lexer->token, skip); \
  1888.                 return lexer->token; \
  1889.             }
  1890. #else
  1891. #define CondReturnTextNode(doc, skip) \
  1892.             if (lexer->txtend > lexer->txtstart) \
  1893.             { \
  1894.                 lexer->token = TextToken(lexer); \
  1895.                 return lexer->token; \
  1896.             }
  1897. #endif
  1898.  
  1899. /*
  1900.   modes for GetToken()
  1901.  
  1902.   MixedContent   -- for elements which don't accept PCDATA
  1903.   Preformatted   -- white space preserved as is
  1904.   IgnoreMarkup   -- for CDATA elements such as script, style
  1905. */
  1906.  
  1907. Node* GetToken( TidyDocImpl* doc, uint mode )
  1908. {
  1909.     Lexer* lexer = doc->lexer;
  1910.     uint c, lastc, badcomment = 0;
  1911.     Bool isempty = no;
  1912.     AttVal *attributes = NULL;
  1913.  
  1914.     if (lexer->pushed)
  1915.     {
  1916.         /* duplicate inlines in preference to pushed text nodes when appropriate */
  1917.         if (lexer->token->type != TextNode || (!lexer->insert && !lexer->inode))
  1918.         {
  1919.             lexer->pushed = no;
  1920.             return lexer->token;
  1921.         }
  1922.     }
  1923.  
  1924.     /* at start of block elements, unclosed inline
  1925.        elements are inserted into the token stream */
  1926.      
  1927.     if (lexer->insert || lexer->inode)
  1928.         return InsertedToken( doc );
  1929.  
  1930.     if (mode == CdataContent)
  1931.     {
  1932.         assert( lexer->parent != NULL );
  1933.         return GetCDATA(doc, lexer->parent);
  1934.     }
  1935.  
  1936.     lexer->lines = doc->docIn->curline;
  1937.     lexer->columns = doc->docIn->curcol;
  1938.     lexer->waswhite = no;
  1939.  
  1940.     lexer->txtstart = lexer->txtend = lexer->lexsize;
  1941.  
  1942.     while ((c = ReadChar(doc->docIn)) != EndOfStream)
  1943.     {
  1944.         if (lexer->insertspace && !(mode & IgnoreWhitespace))
  1945.         {
  1946.             AddCharToLexer(lexer, ' ');
  1947.             lexer->waswhite = yes;
  1948.             lexer->insertspace = no;
  1949.         }
  1950.  
  1951.         if (c == 160 && (mode & Preformatted))
  1952.             c = ' ';
  1953.  
  1954.         AddCharToLexer(lexer, (uint)c);
  1955.  
  1956.         switch (lexer->state)
  1957.         {
  1958.             case LEX_CONTENT:  /* element content */
  1959.  
  1960.                 /*
  1961.                  Discard white space if appropriate. Its cheaper
  1962.                  to do this here rather than in parser methods
  1963.                  for elements that don't have mixed content.
  1964.                 */
  1965.                 if (IsWhite(c) && (mode == IgnoreWhitespace) 
  1966.                       && lexer->lexsize == lexer->txtstart + 1)
  1967.                 {
  1968.                     --(lexer->lexsize);
  1969.                     lexer->waswhite = no;
  1970.                     lexer->lines = doc->docIn->curline;
  1971.                     lexer->columns = doc->docIn->curcol;
  1972.                     continue;
  1973.                 }
  1974.  
  1975.                 if (c == '<')
  1976.                 {
  1977.                     lexer->state = LEX_GT;
  1978.                     continue;
  1979.                 }
  1980.  
  1981.                 if (IsWhite(c))
  1982.                 {
  1983.                     /* was previous character white? */
  1984.                     if (lexer->waswhite)
  1985.                     {
  1986.                         if (mode != Preformatted && mode != IgnoreMarkup)
  1987.                         {
  1988.                             --(lexer->lexsize);
  1989.                             lexer->lines = doc->docIn->curline;
  1990.                             lexer->columns = doc->docIn->curcol;
  1991.                         }
  1992.                     }
  1993.                     else /* prev character wasn't white */
  1994.                     {
  1995.                         lexer->waswhite = yes;
  1996.                         lastc = c;
  1997.  
  1998.                         if (mode != Preformatted && mode != IgnoreMarkup && c != ' ')
  1999.                             ChangeChar(lexer, ' ');
  2000.                     }
  2001.  
  2002.                     continue;
  2003.                 }
  2004.                 else if (c == '&' && mode != IgnoreMarkup)
  2005.                     ParseEntity( doc, mode );
  2006.  
  2007.                 /* this is needed to avoid trimming trailing whitespace */
  2008.                 if (mode == IgnoreWhitespace)
  2009.                     mode = MixedContent;
  2010.  
  2011.                 lexer->waswhite = no;
  2012.                 continue;
  2013.  
  2014.             case LEX_GT:  /* < */
  2015.  
  2016.                 /* check for endtag */
  2017.                 if (c == '/')
  2018.                 {
  2019.                     if ((c = ReadChar(doc->docIn)) == EndOfStream)
  2020.                     {
  2021.                         UngetChar(c, doc->docIn);
  2022.                         continue;
  2023.                     }
  2024.  
  2025.                     AddCharToLexer(lexer, c);
  2026.  
  2027.                     if (IsLetter(c))
  2028.                     {
  2029.                         lexer->lexsize -= 3;
  2030.                         lexer->txtend = lexer->lexsize;
  2031.                         UngetChar(c, doc->docIn);
  2032.                         lexer->state = LEX_ENDTAG;
  2033.                         lexer->lexbuf[lexer->lexsize] = '\0';  /* debug */
  2034.                         doc->docIn->curcol -= 2;
  2035.  
  2036.                         /* if some text before the </ return it now */
  2037.                         if (lexer->txtend > lexer->txtstart)
  2038.                         {
  2039.                             /* trim space character before end tag */
  2040.                             if (mode == IgnoreWhitespace && lexer->lexbuf[lexer->lexsize - 1] == ' ')
  2041.                             {
  2042.                                 lexer->lexsize -= 1;
  2043.                                 lexer->txtend = lexer->lexsize;
  2044.                             }
  2045.                             lexer->token = TextToken(lexer);
  2046. #ifdef TIDY_STORE_ORIGINAL_TEXT
  2047.                             StoreOriginalTextInToken(doc, lexer->token, 3);
  2048. #endif
  2049.                             return lexer->token;
  2050.                         }
  2051.  
  2052.                         continue;       /* no text so keep going */
  2053.                     }
  2054.  
  2055.                     /* otherwise treat as CDATA */
  2056.                     lexer->waswhite = no;
  2057.                     lexer->state = LEX_CONTENT;
  2058.                     continue;
  2059.                 }
  2060.  
  2061.                 if (mode == IgnoreMarkup)
  2062.                 {
  2063.                     /* otherwise treat as CDATA */
  2064.                     lexer->waswhite = no;
  2065.                     lexer->state = LEX_CONTENT;
  2066.                     continue;
  2067.                 }
  2068.  
  2069.                 /*
  2070.                    look out for comments, doctype or marked sections
  2071.                    this isn't quite right, but its getting there ...
  2072.                 */
  2073.                 if (c == '!')
  2074.                 {
  2075.                     c = ReadChar(doc->docIn);
  2076.  
  2077.                     if (c == '-')
  2078.                     {
  2079.                         c = ReadChar(doc->docIn);
  2080.  
  2081.                         if (c == '-')
  2082.                         {
  2083.                             lexer->state = LEX_COMMENT;  /* comment */
  2084.                             lexer->lexsize -= 2;
  2085.                             lexer->txtend = lexer->lexsize;
  2086.  
  2087.                             CondReturnTextNode(doc, 4)
  2088.  
  2089.                             lexer->txtstart = lexer->lexsize;
  2090.                             continue;
  2091.                         }
  2092.  
  2093.                         ReportError(doc, NULL, NULL, MALFORMED_COMMENT );
  2094.                     }
  2095.                     else if (c == 'd' || c == 'D')
  2096.                     {
  2097.                         /* todo: check for complete "<!DOCTYPE" not just <!D */
  2098.  
  2099.                         uint skip = 0;
  2100.  
  2101.                         lexer->state = LEX_DOCTYPE; /* doctype */
  2102.                         lexer->lexsize -= 2;
  2103.                         lexer->txtend = lexer->lexsize;
  2104.                         mode = IgnoreWhitespace;
  2105.  
  2106.                         /* skip until white space or '>' */
  2107.  
  2108.                         for (;;)
  2109.                         {
  2110.                             c = ReadChar(doc->docIn);
  2111.                             ++skip;
  2112.  
  2113.                             if (c == EndOfStream || c == '>')
  2114.                             {
  2115.                                 UngetChar(c, doc->docIn);
  2116.                                 break;
  2117.                             }
  2118.  
  2119.  
  2120.                             if (!IsWhite(c))
  2121.                                 continue;
  2122.  
  2123.                             /* and skip to end of whitespace */
  2124.  
  2125.                             for (;;)
  2126.                             {
  2127.                                 c = ReadChar(doc->docIn);
  2128.                                 ++skip;
  2129.  
  2130.                                 if (c == EndOfStream || c == '>')
  2131.                                 {
  2132.                                     UngetChar(c, doc->docIn);
  2133.                                     break;
  2134.                                 }
  2135.  
  2136.  
  2137.                                 if (IsWhite(c))
  2138.                                     continue;
  2139.  
  2140.                                 UngetChar(c, doc->docIn);
  2141.                                 break;
  2142.                             }
  2143.  
  2144.                             break;
  2145.                         }
  2146.  
  2147.                         CondReturnTextNode(doc, (skip + 3))
  2148.  
  2149.                         lexer->txtstart = lexer->lexsize;
  2150.                         continue;
  2151.                     }
  2152.                     else if (c == '[')
  2153.                     {
  2154.                         /* Word 2000 embeds <![if ...]> ... <![endif]> sequences */
  2155.                         lexer->lexsize -= 2;
  2156.                         lexer->state = LEX_SECTION;
  2157.                         lexer->txtend = lexer->lexsize;
  2158.  
  2159.                         CondReturnTextNode(doc, 2)
  2160.  
  2161.                         lexer->txtstart = lexer->lexsize;
  2162.                         continue;
  2163.                     }
  2164.  
  2165.  
  2166.  
  2167.                     /* else swallow characters up to and including next '>' */
  2168.                     while ((c = ReadChar(doc->docIn)) != '>')
  2169.                     {
  2170.                         if (c == EndOfStream)
  2171.                         {
  2172.                             UngetChar(c, doc->docIn);
  2173.                             break;
  2174.                         }
  2175.                     }
  2176.  
  2177.                     lexer->lexsize -= 2;
  2178.                     lexer->lexbuf[lexer->lexsize] = '\0';
  2179.                     lexer->state = LEX_CONTENT;
  2180.                     continue;
  2181.                 }
  2182.  
  2183.                 /*
  2184.                    processing instructions
  2185.                 */
  2186.  
  2187.                 if (c == '?')
  2188.                 {
  2189.                     lexer->lexsize -= 2;
  2190.                     lexer->state = LEX_PROCINSTR;
  2191.                     lexer->txtend = lexer->lexsize;
  2192.  
  2193.                     CondReturnTextNode(doc, 2)
  2194.  
  2195.                     lexer->txtstart = lexer->lexsize;
  2196.                     continue;
  2197.                 }
  2198.  
  2199.                 /* Microsoft ASP's e.g. <% ... server-code ... %> */
  2200.                 if (c == '%')
  2201.                 {
  2202.                     lexer->lexsize -= 2;
  2203.                     lexer->state = LEX_ASP;
  2204.                     lexer->txtend = lexer->lexsize;
  2205.  
  2206.                     CondReturnTextNode(doc, 2)
  2207.  
  2208.                     lexer->txtstart = lexer->lexsize;
  2209.                     continue;
  2210.                 }
  2211.  
  2212.                 /* Netscapes JSTE e.g. <# ... server-code ... #> */
  2213.                 if (c == '#')
  2214.                 {
  2215.                     lexer->lexsize -= 2;
  2216.                     lexer->state = LEX_JSTE;
  2217.                     lexer->txtend = lexer->lexsize;
  2218.  
  2219.                     CondReturnTextNode(doc, 2)
  2220.  
  2221.                     lexer->txtstart = lexer->lexsize;
  2222.                     continue;
  2223.                 }
  2224.  
  2225.                 /* check for start tag */
  2226.                 if (IsLetter(c))
  2227.                 {
  2228.                     UngetChar(c, doc->docIn);     /* push back letter */
  2229.                     UngetChar('<', doc->docIn);
  2230.                     --(doc->docIn->curcol);
  2231.                     lexer->lexsize -= 2;      /* discard "<" + letter */
  2232.                     lexer->txtend = lexer->lexsize;
  2233.                     lexer->state = LEX_STARTTAG;         /* ready to read tag name */
  2234.  
  2235.                     CondReturnTextNode(doc, 2)
  2236.  
  2237.                     /* lexer->txtstart = lexer->lexsize; missing here? */
  2238.                     continue;       /* no text so keep going */
  2239.                 }
  2240.  
  2241.                 /* fix for bug 762102 */
  2242.                 if (c == '&')
  2243.                 {
  2244.                     UngetChar(c, doc->docIn);
  2245.                     --(lexer->lexsize);
  2246.                 }
  2247.  
  2248.                 /* otherwise treat as CDATA */
  2249.                 lexer->state = LEX_CONTENT;
  2250.                 lexer->waswhite = no;
  2251.                 continue;
  2252.  
  2253.             case LEX_ENDTAG:  /* </letter */
  2254.                 lexer->txtstart = lexer->lexsize - 1;
  2255.                 doc->docIn->curcol += 2;
  2256.                 c = ParseTagName( doc );
  2257.                 lexer->token = TagToken( doc, EndTag );  /* create endtag token */
  2258.                 lexer->lexsize = lexer->txtend = lexer->txtstart;
  2259.  
  2260.                 /* skip to '>' */
  2261.                 while ( c != '>' && c != EndOfStream )
  2262.                 {
  2263.                     c = ReadChar(doc->docIn);
  2264.                 }
  2265.  
  2266.                 if (c == EndOfStream)
  2267.                 {
  2268.                     FreeNode( doc, lexer->token );
  2269.                     continue;
  2270.                 }
  2271.  
  2272.                 lexer->state = LEX_CONTENT;
  2273.                 lexer->waswhite = no;
  2274. #ifdef TIDY_STORE_ORIGINAL_TEXT
  2275.                 StoreOriginalTextInToken(doc, lexer->token, 0); /* hmm... */
  2276. #endif
  2277.                 return lexer->token;  /* the endtag token */
  2278.  
  2279.             case LEX_STARTTAG: /* first letter of tagname */
  2280.                 c = ReadChar(doc->docIn);
  2281.                 ChangeChar(lexer, (tmbchar)c);
  2282.                 lexer->txtstart = lexer->lexsize - 1; /* set txtstart to first letter */
  2283.                 c = ParseTagName( doc );
  2284.                 isempty = no;
  2285.                 attributes = NULL;
  2286.                 lexer->token = TagToken( doc, (isempty ? StartEndTag : StartTag) );
  2287.  
  2288.                 /* parse attributes, consuming closing ">" */
  2289.                 if (c != '>')
  2290.                 {
  2291.                     if (c == '/')
  2292.                         UngetChar(c, doc->docIn);
  2293.  
  2294.                     attributes = ParseAttrs( doc, &isempty );
  2295.                 }
  2296.  
  2297.                 if (isempty)
  2298.                     lexer->token->type = StartEndTag;
  2299.  
  2300.                 lexer->token->attributes = attributes;
  2301.                 lexer->lexsize = lexer->txtend = lexer->txtstart;
  2302.  
  2303.                 /* swallow newline following start tag */
  2304.                 /* special check needed for CRLF sequence */
  2305.                 /* this doesn't apply to empty elements */
  2306.                 /* nor to preformatted content that needs escaping */
  2307.  
  2308.                 if ((mode != Preformatted && ExpectsContent(lexer->token))
  2309.                     || nodeIsBR(lexer->token) || nodeIsHR(lexer->token))
  2310.                 {
  2311.                     c = ReadChar(doc->docIn);
  2312.  
  2313.                     if (c != '\n' && c != '\f')
  2314.                         UngetChar(c, doc->docIn);
  2315.  
  2316.                     lexer->waswhite = yes;  /* to swallow leading whitespace */
  2317.                 }
  2318.                 else
  2319.                     lexer->waswhite = no;
  2320.  
  2321.                 lexer->state = LEX_CONTENT;
  2322.                 if (lexer->token->tag == NULL)
  2323.                     ReportFatal( doc, NULL, lexer->token, UNKNOWN_ELEMENT );
  2324.                 else if ( !cfgBool(doc, TidyXmlTags) )
  2325.                 {
  2326.                     Node* curr = lexer->token;
  2327.                     ConstrainVersion( doc, curr->tag->versions );
  2328.                     
  2329.                     if ( curr->tag->versions & VERS_PROPRIETARY )
  2330.                     {
  2331.                         if ( !cfgBool(doc, TidyMakeClean) ||
  2332.                              ( !nodeIsNOBR(curr) && !nodeIsWBR(curr) ) )
  2333.                         {
  2334.                             ReportError(doc, NULL, curr, PROPRIETARY_ELEMENT );
  2335.  
  2336.                             if ( nodeIsLAYER(curr) )
  2337.                                 doc->badLayout |= USING_LAYER;
  2338.                             else if ( nodeIsSPACER(curr) )
  2339.                                 doc->badLayout |= USING_SPACER;
  2340.                             else if ( nodeIsNOBR(curr) )
  2341.                                 doc->badLayout |= USING_NOBR;
  2342.                         }
  2343.                     }
  2344.  
  2345.                     RepairDuplicateAttributes( doc, curr );
  2346.                 }
  2347. #ifdef TIDY_STORE_ORIGINAL_TEXT
  2348.                 StoreOriginalTextInToken(doc, lexer->token, 0);
  2349. #endif
  2350.                 return lexer->token;  /* return start tag */
  2351.  
  2352.             case LEX_COMMENT:  /* seen <!-- so look for --> */
  2353.  
  2354.                 if (c != '-')
  2355.                     continue;
  2356.  
  2357.                 c = ReadChar(doc->docIn);
  2358.                 AddCharToLexer(lexer, c);
  2359.  
  2360.                 if (c != '-')
  2361.                     continue;
  2362.  
  2363.             end_comment:
  2364.                 c = ReadChar(doc->docIn);
  2365.  
  2366.                 if (c == '>')
  2367.                 {
  2368.                     if (badcomment)
  2369.                         ReportError(doc, NULL, NULL, MALFORMED_COMMENT );
  2370.  
  2371.                     /* do not store closing -- in lexbuf */
  2372.                     lexer->lexsize -= 2;
  2373.                     lexer->txtend = lexer->lexsize;
  2374.                     lexer->lexbuf[lexer->lexsize] = '\0';
  2375.                     lexer->state = LEX_CONTENT;
  2376.                     lexer->waswhite = no;
  2377.                     lexer->token = CommentToken(doc);
  2378.  
  2379.                     /* now look for a line break */
  2380.  
  2381.                     c = ReadChar(doc->docIn);
  2382.  
  2383.                     if (c == '\n')
  2384.                         lexer->token->linebreak = yes;
  2385.                     else
  2386.                         UngetChar(c, doc->docIn);
  2387.  
  2388.                     return lexer->token;
  2389.                 }
  2390.  
  2391.                 /* note position of first such error in the comment */
  2392.                 if (!badcomment)
  2393.                 {
  2394.                     lexer->lines = doc->docIn->curline;
  2395.                     lexer->columns = doc->docIn->curcol - 3;
  2396.                 }
  2397.  
  2398.                 badcomment++;
  2399.  
  2400.                 if ( cfgBool(doc, TidyFixComments) )
  2401.                     lexer->lexbuf[lexer->lexsize - 2] = '=';
  2402.  
  2403.                 AddCharToLexer(lexer, c);
  2404.  
  2405.                 /* if '-' then look for '>' to end the comment */
  2406.                 if (c == '-')
  2407.                     goto end_comment;
  2408.  
  2409.                 /* otherwise continue to look for --> */
  2410.                 lexer->lexbuf[lexer->lexsize - 2] = '=';
  2411.                 continue; 
  2412.  
  2413.             case LEX_DOCTYPE:  /* seen <!d so look for '>' munging whitespace */
  2414.  
  2415.                 /* use ParseDocTypeDecl() to tokenize doctype declaration */
  2416.                 UngetChar(c, doc->docIn);
  2417.                 lexer->lexsize -= 1;
  2418.                 lexer->token = ParseDocTypeDecl(doc);
  2419.  
  2420.                 lexer->txtend = lexer->lexsize;
  2421.                 lexer->lexbuf[lexer->lexsize] = '\0';
  2422.                 lexer->state = LEX_CONTENT;
  2423.                 lexer->waswhite = no;
  2424.  
  2425.                 /* make a note of the version named by the 1st doctype */
  2426.                 if (lexer->doctype == VERS_UNKNOWN && lexer->token && !cfgBool(doc, TidyXmlTags))
  2427.                     lexer->doctype = FindGivenVersion(doc, lexer->token);
  2428.                 return lexer->token;
  2429.  
  2430.             case LEX_PROCINSTR:  /* seen <? so look for '>' */
  2431.                 /* check for PHP preprocessor instructions <?php ... ?> */
  2432.  
  2433.                 if  (lexer->lexsize - lexer->txtstart == 3)
  2434.                 {
  2435.                     if (tmbstrncmp(lexer->lexbuf + lexer->txtstart, "php", 3) == 0)
  2436.                     {
  2437.                         lexer->state = LEX_PHP;
  2438.                         continue;
  2439.                     }
  2440.                 }
  2441.  
  2442.                 if  (lexer->lexsize - lexer->txtstart == 4)
  2443.                 {
  2444.                     if (tmbstrncmp(lexer->lexbuf + lexer->txtstart, "xml", 3) == 0 &&
  2445.                         IsWhite(lexer->lexbuf[lexer->txtstart + 3]))
  2446.                     {
  2447.                         lexer->state = LEX_XMLDECL;
  2448.                         attributes = NULL;
  2449.                         continue;
  2450.                     }
  2451.                 }
  2452.  
  2453.                 if (cfgBool(doc, TidyXmlPIs) || lexer->isvoyager) /* insist on ?> as terminator */
  2454.                 {
  2455.                     if (c != '?')
  2456.                         continue;
  2457.  
  2458.                     /* now look for '>' */
  2459.                     c = ReadChar(doc->docIn);
  2460.  
  2461.                     if (c == EndOfStream)
  2462.                     {
  2463.                         ReportError(doc, NULL, NULL, UNEXPECTED_END_OF_FILE );
  2464.                         UngetChar(c, doc->docIn);
  2465.                         continue;
  2466.                     }
  2467.  
  2468.                     AddCharToLexer(lexer, c);
  2469.                 }
  2470.  
  2471.  
  2472.                 if (c != '>')
  2473.                     continue;
  2474.  
  2475.                 lexer->lexsize -= 1;
  2476.  
  2477.                 if (lexer->lexsize)
  2478.                 {
  2479.                     uint i;
  2480.                     Bool closed;
  2481.  
  2482.                     for (i = 0; i <= lexer->lexsize &&
  2483.                         !IsWhite(lexer->lexbuf[i + lexer->txtstart]); ++i)
  2484.                         /**/;
  2485.  
  2486.                     closed = lexer->lexbuf[lexer->lexsize - 1] == '?';
  2487.  
  2488.                     if (closed)
  2489.                         lexer->lexsize -= 1;
  2490.  
  2491.                     lexer->txtstart += i;
  2492.                     lexer->txtend = lexer->lexsize;
  2493.                     lexer->lexbuf[lexer->lexsize] = '\0';
  2494.  
  2495.                     lexer->token = PIToken(doc);
  2496.                     lexer->token->closed = closed;
  2497.                     lexer->token->element = tmbstrndup(lexer->lexbuf +
  2498.                                                        lexer->txtstart - i, i);
  2499.                 }
  2500.                 else
  2501.                 {
  2502.                     lexer->txtend = lexer->lexsize;
  2503.                     lexer->lexbuf[lexer->lexsize] = '\0';
  2504.                     lexer->token = PIToken(doc);
  2505.                 }
  2506.  
  2507.                 lexer->state = LEX_CONTENT;
  2508.                 lexer->waswhite = no;
  2509.                 return lexer->token;
  2510.  
  2511.             case LEX_ASP:  /* seen <% so look for "%>" */
  2512.                 if (c != '%')
  2513.                     continue;
  2514.  
  2515.                 /* now look for '>' */
  2516.                 c = ReadChar(doc->docIn);
  2517.  
  2518.  
  2519.                 if (c != '>')
  2520.                 {
  2521.                     UngetChar(c, doc->docIn);
  2522.                     continue;
  2523.                 }
  2524.  
  2525.                 lexer->lexsize -= 1;
  2526.                 lexer->txtend = lexer->lexsize;
  2527.                 lexer->lexbuf[lexer->lexsize] = '\0';
  2528.                 lexer->state = LEX_CONTENT;
  2529.                 lexer->waswhite = no;
  2530.                 return lexer->token = AspToken(doc);
  2531.  
  2532.             case LEX_JSTE:  /* seen <# so look for "#>" */
  2533.                 if (c != '#')
  2534.                     continue;
  2535.  
  2536.                 /* now look for '>' */
  2537.                 c = ReadChar(doc->docIn);
  2538.  
  2539.  
  2540.                 if (c != '>')
  2541.                 {
  2542.                     UngetChar(c, doc->docIn);
  2543.                     continue;
  2544.                 }
  2545.  
  2546.                 lexer->lexsize -= 1;
  2547.                 lexer->txtend = lexer->lexsize;
  2548.                 lexer->lexbuf[lexer->lexsize] = '\0';
  2549.                 lexer->state = LEX_CONTENT;
  2550.                 lexer->waswhite = no;
  2551.                 return lexer->token = JsteToken(doc);
  2552.  
  2553.             case LEX_PHP: /* seen "<?php" so look for "?>" */
  2554.                 if (c != '?')
  2555.                     continue;
  2556.  
  2557.                 /* now look for '>' */
  2558.                 c = ReadChar(doc->docIn);
  2559.  
  2560.                 if (c != '>')
  2561.                 {
  2562.                     UngetChar(c, doc->docIn);
  2563.                     continue;
  2564.                 }
  2565.  
  2566.                 lexer->lexsize -= 1;
  2567.                 lexer->txtend = lexer->lexsize;
  2568.                 lexer->lexbuf[lexer->lexsize] = '\0';
  2569.                 lexer->state = LEX_CONTENT;
  2570.                 lexer->waswhite = no;
  2571.                 return lexer->token = PhpToken(doc);
  2572.  
  2573.             case LEX_XMLDECL: /* seen "<?xml" so look for "?>" */
  2574.  
  2575.                 if (IsWhite(c) && c != '?')
  2576.                     continue;
  2577.  
  2578.                 /* get pseudo-attribute */
  2579.                 if (c != '?')
  2580.                 {
  2581.                     tmbstr name;
  2582.                     Node *asp, *php;
  2583.                     AttVal *av = NULL;
  2584.                     int pdelim = 0;
  2585.                     isempty = no;
  2586.  
  2587.                     UngetChar(c, doc->docIn);
  2588.  
  2589.                     name = ParseAttribute( doc, &isempty, &asp, &php );
  2590.  
  2591.                     if (!name)
  2592.                     {
  2593.                         /* fix for http://tidy.sf.net/bug/788031 */
  2594.                         lexer->lexsize -= 1;
  2595.                         lexer->txtend = lexer->txtstart;
  2596.                         lexer->lexbuf[lexer->txtend] = '\0';
  2597.                         lexer->state = LEX_CONTENT;
  2598.                         lexer->waswhite = no;
  2599.                         lexer->token = XmlDeclToken(doc);
  2600.                         lexer->token->attributes = attributes;
  2601.                         return lexer->token;
  2602.                     }
  2603.  
  2604.                     av = NewAttribute();
  2605.                     av->attribute = name;
  2606.                     av->value = ParseValue( doc, name, yes, &isempty, &pdelim );
  2607.                     av->delim = pdelim;
  2608.                     av->dict = FindAttribute( doc, av );
  2609.  
  2610.                     AddAttrToList( &attributes, av );
  2611.                     /* continue; */
  2612.                 }
  2613.  
  2614.                 /* now look for '>' */
  2615.                 c = ReadChar(doc->docIn);
  2616.  
  2617.                 if (c != '>')
  2618.                 {
  2619.                     UngetChar(c, doc->docIn);
  2620.                     continue;
  2621.                 }
  2622.                 lexer->lexsize -= 1;
  2623.                 lexer->txtend = lexer->txtstart;
  2624.                 lexer->lexbuf[lexer->txtend] = '\0';
  2625.                 lexer->state = LEX_CONTENT;
  2626.                 lexer->waswhite = no;
  2627.                 lexer->token = XmlDeclToken(doc);
  2628.                 lexer->token->attributes = attributes;
  2629.                 return lexer->token;
  2630.  
  2631.             case LEX_SECTION: /* seen "<![" so look for "]>" */
  2632.                 if (c == '[')
  2633.                 {
  2634.                     if (lexer->lexsize == (lexer->txtstart + 6) &&
  2635.                         tmbstrncmp(lexer->lexbuf+lexer->txtstart, "CDATA[", 6) == 0)
  2636.                     {
  2637.                         lexer->state = LEX_CDATA;
  2638.                         lexer->lexsize -= 6;
  2639.                         continue;
  2640.                     }
  2641.                 }
  2642.  
  2643.                 if (c != ']')
  2644.                     continue;
  2645.  
  2646.                 /* now look for '>' */
  2647.                 c = ReadChar(doc->docIn);
  2648.  
  2649.                 if (c != '>')
  2650.                 {
  2651.                     UngetChar(c, doc->docIn);
  2652.                     continue;
  2653.                 }
  2654.  
  2655.                 lexer->lexsize -= 1;
  2656.                 lexer->txtend = lexer->lexsize;
  2657.                 lexer->lexbuf[lexer->lexsize] = '\0';
  2658.                 lexer->state = LEX_CONTENT;
  2659.                 lexer->waswhite = no;
  2660.                 return lexer->token = SectionToken(doc);
  2661.  
  2662.             case LEX_CDATA: /* seen "<![CDATA[" so look for "]]>" */
  2663.                 if (c != ']')
  2664.                     continue;
  2665.  
  2666.                 /* now look for ']' */
  2667.                 c = ReadChar(doc->docIn);
  2668.  
  2669.                 if (c != ']')
  2670.                 {
  2671.                     UngetChar(c, doc->docIn);
  2672.                     continue;
  2673.                 }
  2674.  
  2675.                 /* now look for '>' */
  2676.                 c = ReadChar(doc->docIn);
  2677.  
  2678.                 if (c != '>')
  2679.                 {
  2680.                     UngetChar(c, doc->docIn);
  2681.                     continue;
  2682.                 }
  2683.  
  2684.                 lexer->lexsize -= 1;
  2685.                 lexer->txtend = lexer->lexsize;
  2686.                 lexer->lexbuf[lexer->lexsize] = '\0';
  2687.                 lexer->state = LEX_CONTENT;
  2688.                 lexer->waswhite = no;
  2689.                 return lexer->token = CDATAToken(doc);
  2690.         }
  2691.     }
  2692.  
  2693.     if (lexer->state == LEX_CONTENT)  /* text string */
  2694.     {
  2695.         lexer->txtend = lexer->lexsize;
  2696.  
  2697.         if (lexer->txtend > lexer->txtstart)
  2698.         {
  2699.             UngetChar(c, doc->docIn);
  2700.  
  2701.             if (lexer->lexbuf[lexer->lexsize - 1] == ' ')
  2702.             {
  2703.                 lexer->lexsize -= 1;
  2704.                 lexer->txtend = lexer->lexsize;
  2705.             }
  2706.             lexer->token = TextToken(lexer);
  2707. #ifdef TIDY_STORE_ORIGINAL_TEXT
  2708.             StoreOriginalTextInToken(doc, lexer->token, 0); /* ? */
  2709. #endif
  2710.             return lexer->token;
  2711.         }
  2712.     }
  2713.     else if (lexer->state == LEX_COMMENT) /* comment */
  2714.     {
  2715.         if (c == EndOfStream)
  2716.             ReportError(doc, NULL, NULL, MALFORMED_COMMENT );
  2717.  
  2718.         lexer->txtend = lexer->lexsize;
  2719.         lexer->lexbuf[lexer->lexsize] = '\0';
  2720.         lexer->state = LEX_CONTENT;
  2721.         lexer->waswhite = no;
  2722.         return lexer->token = CommentToken(doc);
  2723.     }
  2724.  
  2725.     return 0;
  2726. }
  2727.  
  2728. static void MapStr( ctmbstr str, uint code )
  2729. {
  2730.     while ( *str )
  2731.     {
  2732.         uint i = (byte) *str++;
  2733.         lexmap[i] |= code;
  2734.     }
  2735. }
  2736.  
  2737. void InitMap(void)
  2738. {
  2739.     MapStr("\r\n\f", newline|white);
  2740.     MapStr(" \t", white);
  2741.     MapStr("-.:_", namechar);
  2742.     MapStr("0123456789", digit|namechar);
  2743.     MapStr("abcdefghijklmnopqrstuvwxyz", lowercase|letter|namechar);
  2744.     MapStr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", uppercase|letter|namechar);
  2745. }
  2746.  
  2747. /*
  2748.  parser for ASP within start tags
  2749.  
  2750.  Some people use ASP for to customize attributes
  2751.  Tidy isn't really well suited to dealing with ASP
  2752.  This is a workaround for attributes, but won't
  2753.  deal with the case where the ASP is used to tailor
  2754.  the attribute value. Here is an example of a work
  2755.  around for using ASP in attribute values:
  2756.  
  2757.   href='<%=rsSchool.Fields("ID").Value%>'
  2758.  
  2759.  where the ASP that generates the attribute value
  2760.  is masked from Tidy by the quotemarks.
  2761.  
  2762. */
  2763.  
  2764. static Node *ParseAsp( TidyDocImpl* doc )
  2765. {
  2766.     Lexer* lexer = doc->lexer;
  2767.     uint c;
  2768.     Node *asp = NULL;
  2769.  
  2770.     lexer->txtstart = lexer->lexsize;
  2771.  
  2772.     for (;;)
  2773.     {
  2774.         if ((c = ReadChar(doc->docIn)) == EndOfStream)
  2775.             break;
  2776.  
  2777.         AddCharToLexer(lexer, c);
  2778.  
  2779.  
  2780.         if (c != '%')
  2781.             continue;
  2782.  
  2783.         if ((c = ReadChar(doc->docIn)) == EndOfStream)
  2784.             break;
  2785.  
  2786.         AddCharToLexer(lexer, c);
  2787.  
  2788.         if (c == '>')
  2789.         {
  2790.             lexer->lexsize -= 2;
  2791.             break;
  2792.         }
  2793.     }
  2794.  
  2795.     lexer->txtend = lexer->lexsize;
  2796.     if (lexer->txtend > lexer->txtstart)
  2797.         asp = AspToken(doc);
  2798.  
  2799.     lexer->txtstart = lexer->txtend;
  2800.     return asp;
  2801. }   
  2802.  
  2803.  
  2804. /*
  2805.  PHP is like ASP but is based upon XML
  2806.  processing instructions, e.g. <?php ... ?>
  2807. */
  2808. static Node *ParsePhp( TidyDocImpl* doc )
  2809. {
  2810.     Lexer* lexer = doc->lexer;
  2811.     uint c;
  2812.     Node *php = NULL;
  2813.  
  2814.     lexer->txtstart = lexer->lexsize;
  2815.  
  2816.     for (;;)
  2817.     {
  2818.         if ((c = ReadChar(doc->docIn)) == EndOfStream)
  2819.             break;
  2820.  
  2821.         AddCharToLexer(lexer, c);
  2822.  
  2823.  
  2824.         if (c != '?')
  2825.             continue;
  2826.  
  2827.         if ((c = ReadChar(doc->docIn)) == EndOfStream)
  2828.             break;
  2829.  
  2830.         AddCharToLexer(lexer, c);
  2831.  
  2832.         if (c == '>')
  2833.         {
  2834.             lexer->lexsize -= 2;
  2835.             break;
  2836.         }
  2837.     }
  2838.  
  2839.     lexer->txtend = lexer->lexsize;
  2840.     if (lexer->txtend > lexer->txtstart)
  2841.         php = PhpToken(doc);
  2842.  
  2843.     lexer->txtstart = lexer->txtend;
  2844.     return php;
  2845. }   
  2846.  
  2847. /* consumes the '>' terminating start tags */
  2848. static tmbstr  ParseAttribute( TidyDocImpl* doc, Bool *isempty,
  2849.                               Node **asp, Node **php)
  2850. {
  2851.     Lexer* lexer = doc->lexer;
  2852.     int start, len = 0;
  2853.     tmbstr attr = NULL;
  2854.     uint c, lastc;
  2855.  
  2856.     *asp = NULL;  /* clear asp pointer */
  2857.     *php = NULL;  /* clear php pointer */
  2858.  
  2859.  /* skip white space before the attribute */
  2860.  
  2861.     for (;;)
  2862.     {
  2863.         c = ReadChar( doc->docIn );
  2864.  
  2865.  
  2866.         if (c == '/')
  2867.         {
  2868.             c = ReadChar( doc->docIn );
  2869.  
  2870.             if (c == '>')
  2871.             {
  2872.                 *isempty = yes;
  2873.                 return NULL;
  2874.             }
  2875.  
  2876.             UngetChar(c, doc->docIn);
  2877.             c = '/';
  2878.             break;
  2879.         }
  2880.  
  2881.         if (c == '>')
  2882.             return NULL;
  2883.  
  2884.         if (c =='<')
  2885.         {
  2886.             c = ReadChar(doc->docIn);
  2887.  
  2888.             if (c == '%')
  2889.             {
  2890.                 *asp = ParseAsp( doc );
  2891.                 return NULL;
  2892.             }
  2893.             else if (c == '?')
  2894.             {
  2895.                 *php = ParsePhp( doc );
  2896.                 return NULL;
  2897.             }
  2898.  
  2899.             UngetChar(c, doc->docIn);
  2900.             UngetChar('<', doc->docIn);
  2901.             ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_GT );
  2902.             return NULL;
  2903.         }
  2904.  
  2905.         if (c == '=')
  2906.         {
  2907.             ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_EQUALSIGN );
  2908.             continue;
  2909.         }
  2910.  
  2911.         if (c == '"' || c == '\'')
  2912.         {
  2913.             ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_QUOTEMARK );
  2914.             continue;
  2915.         }
  2916.  
  2917.         if (c == EndOfStream)
  2918.         {
  2919.             ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_END_OF_FILE_ATTR );
  2920.             UngetChar(c, doc->docIn);
  2921.             return NULL;
  2922.         }
  2923.  
  2924.  
  2925.         if (!IsWhite(c))
  2926.            break;
  2927.     }
  2928.  
  2929.     start = lexer->lexsize;
  2930.     lastc = c;
  2931.  
  2932.     for (;;)
  2933.     {
  2934.      /* but push back '=' for parseValue() */
  2935.         if (c == '=' || c == '>')
  2936.         {
  2937.             UngetChar(c, doc->docIn);
  2938.             break;
  2939.         }
  2940.  
  2941.         if (c == '<' || c == EndOfStream)
  2942.         {
  2943.             UngetChar(c, doc->docIn);
  2944.             break;
  2945.         }
  2946.  
  2947.         if (lastc == '-' && (c == '"' || c == '\''))
  2948.         {
  2949.             lexer->lexsize--;
  2950.             --len;
  2951.             UngetChar(c, doc->docIn);
  2952.             break;
  2953.         }
  2954.  
  2955.         if (IsWhite(c))
  2956.             break;
  2957.  
  2958.         /* what should be done about non-namechar characters? */
  2959.         /* currently these are incorporated into the attr name */
  2960.  
  2961.         if ( !cfgBool(doc, TidyXmlTags) && IsUpper(c) )
  2962.             c = ToLower(c);
  2963.  
  2964.         AddCharToLexer( lexer, c );
  2965.         lastc = c;
  2966.         c = ReadChar(doc->docIn);
  2967.     }
  2968.  
  2969.     /* handle attribute names with multibyte chars */
  2970.     len = lexer->lexsize - start;
  2971.     attr = (len > 0 ? tmbstrndup(lexer->lexbuf+start, len) : NULL);
  2972.     lexer->lexsize = start;
  2973.     return attr;
  2974. }
  2975.  
  2976. /*
  2977.  invoked when < is seen in place of attribute value
  2978.  but terminates on whitespace if not ASP, PHP or Tango
  2979.  this routine recognizes ' and " quoted strings
  2980. */
  2981. static int ParseServerInstruction( TidyDocImpl* doc )
  2982. {
  2983.     Lexer* lexer = doc->lexer;
  2984.     int c, delim = '"';
  2985.     Bool isrule = no;
  2986.  
  2987.     c = ReadChar(doc->docIn);
  2988.     AddCharToLexer(lexer, c);
  2989.  
  2990.     /* check for ASP, PHP or Tango */
  2991.     if (c == '%' || c == '?' || c == '@')
  2992.         isrule = yes;
  2993.  
  2994.     for (;;)
  2995.     {
  2996.         c = ReadChar(doc->docIn);
  2997.  
  2998.         if (c == EndOfStream)
  2999.             break;
  3000.  
  3001.         if (c == '>')
  3002.         {
  3003.             if (isrule)
  3004.                 AddCharToLexer(lexer, c);
  3005.             else
  3006.                 UngetChar(c, doc->docIn);
  3007.  
  3008.             break;
  3009.         }
  3010.  
  3011.         /* if not recognized as ASP, PHP or Tango */
  3012.         /* then also finish value on whitespace */
  3013.         if (!isrule)
  3014.         {
  3015.             if (IsWhite(c))
  3016.                 break;
  3017.         }
  3018.  
  3019.         AddCharToLexer(lexer, c);
  3020.  
  3021.         if (c == '"')
  3022.         {
  3023.             do
  3024.             {
  3025.                 c = ReadChar(doc->docIn);
  3026.                 if (c == EndOfStream) /* #427840 - fix by Terry Teague 30 Jun 01 */
  3027.                 {
  3028.                     ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_END_OF_FILE_ATTR );
  3029.                     UngetChar(c, doc->docIn);
  3030.                     return 0;
  3031.                 }
  3032.                 if (c == '>') /* #427840 - fix by Terry Teague 30 Jun 01 */
  3033.                 {
  3034.                     UngetChar(c, doc->docIn);
  3035.                     ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_GT );
  3036.                     return 0;
  3037.                 }
  3038.                 AddCharToLexer(lexer, c);
  3039.             }
  3040.             while (c != '"');
  3041.             delim = '\'';
  3042.             continue;
  3043.         }
  3044.  
  3045.         if (c == '\'')
  3046.         {
  3047.             do
  3048.             {
  3049.                 c = ReadChar(doc->docIn);
  3050.                 if (c == EndOfStream) /* #427840 - fix by Terry Teague 30 Jun 01 */
  3051.                 {
  3052.                     ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_END_OF_FILE_ATTR );
  3053.                     UngetChar(c, doc->docIn);
  3054.                     return 0;
  3055.                 }
  3056.                 if (c == '>') /* #427840 - fix by Terry Teague 30 Jun 01 */
  3057.                 {
  3058.                     UngetChar(c, doc->docIn);
  3059.                     ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_GT );
  3060.                     return 0;
  3061.                 }
  3062.                 AddCharToLexer(lexer, c);
  3063.             }
  3064.             while (c != '\'');
  3065.         }
  3066.     }
  3067.  
  3068.     return delim;
  3069. }
  3070.  
  3071. /* values start with "=" or " = " etc. */
  3072. /* doesn't consume the ">" at end of start tag */
  3073.  
  3074. static tmbstr ParseValue( TidyDocImpl* doc, ctmbstr name,
  3075.                     Bool foldCase, Bool *isempty, int *pdelim)
  3076. {
  3077.     Lexer* lexer = doc->lexer;
  3078.     int len = 0, start;
  3079.     Bool seen_gt = no;
  3080.     Bool munge = yes;
  3081.     uint c, lastc, delim, quotewarning;
  3082.     tmbstr value;
  3083.  
  3084.     delim = (tmbchar) 0;
  3085.     *pdelim = '"';
  3086.  
  3087.     /*
  3088.      Henry Zrepa reports that some folk are using the
  3089.      embed element with script attributes where newlines
  3090.      are significant and must be preserved
  3091.     */
  3092.     if ( cfgBool(doc, TidyLiteralAttribs) )
  3093.         munge = no;
  3094.  
  3095.  /* skip white space before the '=' */
  3096.  
  3097.     for (;;)
  3098.     {
  3099.         c = ReadChar(doc->docIn);
  3100.  
  3101.         if (c == EndOfStream)
  3102.         {
  3103.             UngetChar(c, doc->docIn);
  3104.             break;
  3105.         }
  3106.  
  3107.         if (!IsWhite(c))
  3108.            break;
  3109.     }
  3110.  
  3111. /*
  3112.   c should be '=' if there is a value
  3113.   other legal possibilities are white
  3114.   space, '/' and '>'
  3115. */
  3116.  
  3117.     if (c != '=' && c != '"' && c != '\'')
  3118.     {
  3119.         UngetChar(c, doc->docIn);
  3120.         return NULL;
  3121.     }
  3122.  
  3123.  /* skip white space after '=' */
  3124.  
  3125.     for (;;)
  3126.     {
  3127.         c = ReadChar(doc->docIn);
  3128.  
  3129.         if (c == EndOfStream)
  3130.         {
  3131.             UngetChar(c, doc->docIn);
  3132.             break;
  3133.         }
  3134.  
  3135.         if (!IsWhite(c))
  3136.            break;
  3137.     }
  3138.  
  3139.  /* check for quote marks */
  3140.  
  3141.     if (c == '"' || c == '\'')
  3142.         delim = c;
  3143.     else if (c == '<')
  3144.     {
  3145.         start = lexer->lexsize;
  3146.         AddCharToLexer(lexer, c);
  3147.         *pdelim = ParseServerInstruction( doc );
  3148.         len = lexer->lexsize - start;
  3149.         lexer->lexsize = start;
  3150.         return (len > 0 ? tmbstrndup(lexer->lexbuf+start, len) : NULL);
  3151.     }
  3152.     else
  3153.         UngetChar(c, doc->docIn);
  3154.  
  3155.  /*
  3156.    and read the value string
  3157.    check for quote mark if needed
  3158.  */
  3159.  
  3160.     quotewarning = 0;
  3161.     start = lexer->lexsize;
  3162.     c = '\0';
  3163.  
  3164.     for (;;)
  3165.     {
  3166.         lastc = c;  /* track last character */
  3167.         c = ReadChar(doc->docIn);
  3168.  
  3169.         if (c == EndOfStream)
  3170.         {
  3171.             ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_END_OF_FILE_ATTR );
  3172.             UngetChar(c, doc->docIn);
  3173.             break;
  3174.         }
  3175.  
  3176.         if (delim == (tmbchar)0)
  3177.         {
  3178.             if (c == '>')
  3179.             {
  3180.                 UngetChar(c, doc->docIn);
  3181.                 break;
  3182.             }
  3183.  
  3184.             if (c == '"' || c == '\'')
  3185.             {
  3186.                 uint q = c;
  3187.  
  3188.                 ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_QUOTEMARK );
  3189.  
  3190.                 /* handle <input onclick=s("btn1")> and <a title=foo""">...</a> */
  3191.                 /* this doesn't handle <a title=foo"/> which browsers treat as  */
  3192.                 /* 'foo"/' nor  <a title=foo" /> which browser treat as 'foo"'  */
  3193.                 
  3194.                 c = ReadChar(doc->docIn);
  3195.                 if (c == '>')
  3196.                 {
  3197.                     AddCharToLexer(lexer, q);
  3198.                     UngetChar(c, doc->docIn);
  3199.                     break;
  3200.                 }
  3201.                 else
  3202.                 {
  3203.                     UngetChar(c, doc->docIn);
  3204.                     c = q;
  3205.                 }
  3206.             }
  3207.  
  3208.             if (c == '<')
  3209.             {
  3210.                 UngetChar(c, doc->docIn);
  3211.                 c = '>';
  3212.                 UngetChar(c, doc->docIn);
  3213.                 ReportAttrError( doc, lexer->token, NULL, UNEXPECTED_GT );
  3214.                 break;
  3215.             }
  3216.  
  3217.             /*
  3218.              For cases like <br clear=all/> need to avoid treating /> as
  3219.              part of the attribute value, however care is needed to avoid
  3220.              so treating <a href=http://www.acme.com/> in this way, which
  3221.              would map the <a> tag to <a href="http://www.acme.com"/>
  3222.             */
  3223.             if (c == '/')
  3224.             {
  3225.                 /* peek ahead in case of /> */
  3226.                 c = ReadChar(doc->docIn);
  3227.  
  3228.                 if ( c == '>' && !IsUrl(doc, name) )
  3229.                 {
  3230.                     *isempty = yes;
  3231.                     UngetChar(c, doc->docIn);
  3232.                     break;
  3233.                 }
  3234.  
  3235.                 /* unget peeked character */
  3236.                 UngetChar(c, doc->docIn);
  3237.                 c = '/';
  3238.             }
  3239.         }
  3240.         else  /* delim is '\'' or '"' */
  3241.         {
  3242.             if (c == delim)
  3243.                 break;
  3244.  
  3245.             if (c == '\n' || c == '<' || c == '>')
  3246.                 ++quotewarning;
  3247.  
  3248.             if (c == '>')
  3249.                 seen_gt = yes;
  3250.         }
  3251.  
  3252.         if (c == '&')
  3253.         {
  3254.             AddCharToLexer(lexer, c);
  3255.             ParseEntity( doc, 0 );
  3256.             if (lexer->lexbuf[lexer->lexsize - 1] == '\n' && munge)
  3257.                 ChangeChar(lexer, ' ');
  3258.             continue;
  3259.         }
  3260.  
  3261.         /*
  3262.          kludge for JavaScript attribute values
  3263.          with line continuations in string literals
  3264.         */
  3265.         if (c == '\\')
  3266.         {
  3267.             c = ReadChar(doc->docIn);
  3268.  
  3269.             if (c != '\n')
  3270.             {
  3271.                 UngetChar(c, doc->docIn);
  3272.                 c = '\\';
  3273.             }
  3274.         }
  3275.  
  3276.         if (IsWhite(c))
  3277.         {
  3278.             if ( delim == 0 )
  3279.                 break;
  3280.  
  3281.             if (munge)
  3282.             {
  3283.                 /* discard line breaks in quoted URLs */ 
  3284.                 /* #438650 - fix by Randy Waki */
  3285.                 if ( c == '\n' && IsUrl(doc, name) )
  3286.                 {
  3287.                     /* warn that we discard this newline */
  3288.                     ReportAttrError( doc, lexer->token, NULL, NEWLINE_IN_URI);
  3289.                     continue;
  3290.                 }
  3291.                 
  3292.                 c = ' ';
  3293.  
  3294.                 if (lastc == ' ')
  3295.                     continue;
  3296.             }
  3297.         }
  3298.         else if (foldCase && IsUpper(c))
  3299.             c = ToLower(c);
  3300.  
  3301.         AddCharToLexer(lexer, c);
  3302.     }
  3303.  
  3304.     if (quotewarning > 10 && seen_gt && munge)
  3305.     {
  3306.         /*
  3307.            there is almost certainly a missing trailing quote mark
  3308.            as we have see too many newlines, < or > characters.
  3309.  
  3310.            an exception is made for Javascript attributes and the
  3311.            javascript URL scheme which may legitimately include < and >,
  3312.            and for attributes starting with "<xml " as generated by
  3313.            Microsoft Office.
  3314.         */
  3315.         if ( !IsScript(doc, name) &&
  3316.              !(IsUrl(doc, name) && tmbstrncmp(lexer->lexbuf+start, "javascript:", 11) == 0) &&
  3317.              !(tmbstrncmp(lexer->lexbuf+start, "<xml ", 5) == 0)
  3318.            )
  3319.             ReportFatal( doc, NULL, NULL, SUSPECTED_MISSING_QUOTE ); 
  3320.     }
  3321.  
  3322.     len = lexer->lexsize - start;
  3323.     lexer->lexsize = start;
  3324.  
  3325.  
  3326.     if (len > 0 || delim)
  3327.     {
  3328.         /* ignore leading and trailing white space for all but title and */
  3329.         /* alt attributes unless --literal-attributes is set to yes      */
  3330.  
  3331.         if (munge && tmbstrcasecmp(name, "alt") && tmbstrcasecmp(name, "title"))
  3332.         {
  3333.             while (IsWhite(lexer->lexbuf[start+len-1]))
  3334.                 --len;
  3335.  
  3336.             while (IsWhite(lexer->lexbuf[start]) && start < len)
  3337.             {
  3338.                 ++start;
  3339.                 --len;
  3340.             }
  3341.         }
  3342.  
  3343.         value = tmbstrndup(lexer->lexbuf + start, len);
  3344.     }
  3345.     else
  3346.         value = NULL;
  3347.  
  3348.     /* note delimiter if given */
  3349.     *pdelim = (delim ? delim : '"');
  3350.  
  3351.     return value;
  3352. }
  3353.  
  3354. /* attr must be non-NULL */
  3355. Bool IsValidAttrName( ctmbstr attr )
  3356. {
  3357.     uint i, c = attr[0];
  3358.  
  3359.     /* first character should be a letter */
  3360.     if (!IsLetter(c))
  3361.         return no;
  3362.  
  3363.     /* remaining characters should be namechars */
  3364.     for( i = 1; i < tmbstrlen(attr); i++)
  3365.     {
  3366.         c = attr[i];
  3367.  
  3368.         if (IsNamechar(c))
  3369.             continue;
  3370.  
  3371.         return no;
  3372.     }
  3373.  
  3374.     return yes;
  3375. }
  3376.  
  3377. /* create a new attribute */
  3378. AttVal *NewAttribute(void)
  3379. {
  3380.     AttVal *av = (AttVal*) MemAlloc( sizeof(AttVal) );
  3381.     ClearMemory( av, sizeof(AttVal) );
  3382.     return av;
  3383. }
  3384.  
  3385. /* create a new attribute with given name and value */
  3386. AttVal* NewAttributeEx( ctmbstr name, ctmbstr value )
  3387. {
  3388.     AttVal *av = NewAttribute();
  3389.     av->attribute = tmbstrdup(name);
  3390.     av->value = tmbstrdup(value);
  3391.     return av;
  3392. }
  3393.  
  3394.  
  3395. /* swallows closing '>' */
  3396.  
  3397. static void AddAttrToList( AttVal** list, AttVal* av )
  3398. {
  3399.   if ( *list == NULL )
  3400.     *list = av;
  3401.   else
  3402.   {
  3403.     AttVal* here = *list;
  3404.     while ( here->next )
  3405.       here = here->next;
  3406.     here->next = av;
  3407.   }
  3408. }
  3409.  
  3410. AttVal* ParseAttrs( TidyDocImpl* doc, Bool *isempty )
  3411. {
  3412.     Lexer* lexer = doc->lexer;
  3413.     AttVal *av, *list;
  3414.     tmbstr value;
  3415.     int delim;
  3416.     Node *asp, *php;
  3417.  
  3418.     list = NULL;
  3419.  
  3420.     while ( !EndOfInput(doc) )
  3421.     {
  3422.         tmbstr attribute = ParseAttribute( doc, isempty, &asp, &php );
  3423.  
  3424.         if (attribute == NULL)
  3425.         {
  3426.             /* check if attributes are created by ASP markup */
  3427.             if (asp)
  3428.             {
  3429.                 av = NewAttribute();
  3430.                 av->asp = asp;
  3431.                 AddAttrToList( &list, av ); 
  3432.                 continue;
  3433.             }
  3434.  
  3435.             /* check if attributes are created by PHP markup */
  3436.             if (php)
  3437.             {
  3438.                 av = NewAttribute();
  3439.                 av->php = php;
  3440.                 AddAttrToList( &list, av ); 
  3441.                 continue;
  3442.             }
  3443.  
  3444.             break;
  3445.         }
  3446.  
  3447.         value = ParseValue( doc, attribute, no, isempty, &delim );
  3448.  
  3449.         if (attribute && (IsValidAttrName(attribute) ||
  3450.             (cfgBool(doc, TidyXmlTags) && IsValidXMLAttrName(attribute))))
  3451.         {
  3452.             av = NewAttribute();
  3453.             av->delim = delim;
  3454.             av->attribute = attribute;
  3455.             av->value = value;
  3456.             av->dict = FindAttribute( doc, av );
  3457.             AddAttrToList( &list, av ); 
  3458.         }
  3459.         else
  3460.         {
  3461.             av = NewAttribute();
  3462.             av->attribute = attribute;
  3463.             av->value = value;
  3464.  
  3465.             if (LastChar(attribute) == '"')
  3466.                 ReportAttrError( doc, lexer->token, av, MISSING_QUOTEMARK);
  3467.             else if (value == NULL)
  3468.                 ReportAttrError(doc, lexer->token, av, MISSING_ATTR_VALUE);
  3469.             else
  3470.                 ReportAttrError(doc, lexer->token, av, INVALID_ATTRIBUTE);
  3471.  
  3472.             FreeAttribute( doc, av );
  3473.         }
  3474.     }
  3475.  
  3476.     return list;
  3477. }
  3478.  
  3479. /*
  3480.   Returns document type declarations like
  3481.  
  3482.   <!DOCTYPE foo PUBLIC "fpi" "sysid">
  3483.   <!DOCTYPE bar SYSTEM "sysid">
  3484.   <!DOCTYPE baz [ <!ENTITY ouml "ö"> ]>
  3485.  
  3486.   as
  3487.  
  3488.   <foo PUBLIC="fpi" SYSTEM="sysid" />
  3489.   <bar SYSTEM="sysid" />
  3490.   <baz> <!ENTITY ouml "&#246"> </baz>
  3491. */
  3492. static Node *ParseDocTypeDecl(TidyDocImpl* doc)
  3493. {
  3494.     Lexer *lexer = doc->lexer;
  3495.     int start = lexer->lexsize;
  3496.     int state = DT_DOCTYPENAME;
  3497.     uint c;
  3498.     uint delim = 0;
  3499.     Bool hasfpi = yes;
  3500.  
  3501.     Node* node = NewNode(lexer);
  3502.     node->type = DocTypeTag;
  3503.     node->start = lexer->txtstart;
  3504.     node->end = lexer->txtend;
  3505.  
  3506.     lexer->waswhite = no;
  3507.  
  3508.     /* todo: reset lexer->lexsize when appropriate to avoid wasting memory */
  3509.  
  3510.     while ((c = ReadChar(doc->docIn)) != EndOfStream)
  3511.     {
  3512.         /* convert newlines to spaces */
  3513.         if (state != DT_INTSUBSET)
  3514.             c = c == '\n' ? ' ' : c;
  3515.  
  3516.         /* convert white-space sequences to single space character */
  3517.         if (IsWhite(c) && state != DT_INTSUBSET)
  3518.         {
  3519.             if (!lexer->waswhite)
  3520.             {
  3521.                 AddCharToLexer(lexer, (uint)c);
  3522.                 lexer->waswhite = yes;
  3523.             }
  3524.             else
  3525.             {
  3526.                 /* discard space */
  3527.                 continue;
  3528.             }
  3529.         }
  3530.         else
  3531.         {
  3532.             AddCharToLexer(lexer, (uint)c);
  3533.             lexer->waswhite = no;
  3534.         }
  3535.  
  3536.         switch(state)
  3537.         {
  3538.         case DT_INTERMEDIATE:
  3539.             /* determine what's next */
  3540.             if (ToUpper(c) == 'P' || ToUpper(c) == 'S')
  3541.             {
  3542.                 start = lexer->lexsize - 1;
  3543.                 state = DT_PUBLICSYSTEM;
  3544.                 continue;
  3545.             }
  3546.             else if (c == '[')
  3547.             {
  3548.                 start = lexer->lexsize;
  3549.                 state = DT_INTSUBSET;
  3550.                 continue;
  3551.             }
  3552.             else if (c == '\'' || c == '"')
  3553.             {
  3554.                 start = lexer->lexsize;
  3555.                 delim = c;
  3556.                 state = DT_QUOTEDSTRING;
  3557.                 continue;
  3558.             }
  3559.             else if (c == '>')
  3560.             {
  3561.                 AttVal* si;
  3562.  
  3563.                 node->end = --(lexer->lexsize);
  3564.  
  3565.                 si = GetAttrByName(node, "SYSTEM");
  3566.                 if (si)
  3567.                     CheckUrl(doc, node, si);
  3568.  
  3569.                 if (!node->element || !IsValidXMLElemName(node->element))
  3570.                 {
  3571.                     ReportError(doc, NULL, NULL, MALFORMED_DOCTYPE);
  3572.                     FreeNode(doc, node);
  3573.                     return NULL;
  3574.                 }
  3575. #ifdef TIDY_STORE_ORIGINAL_TEXT
  3576.                 StoreOriginalTextInToken(doc, node, 0);
  3577. #endif
  3578.                 return node;
  3579.             }
  3580.             else
  3581.             {
  3582.                 /* error */
  3583.             }
  3584.             break;
  3585.         case DT_DOCTYPENAME:
  3586.             /* read document type name */
  3587.             if (IsWhite(c) || c == '>' || c == '[')
  3588.             {
  3589.                 node->element = tmbstrndup(lexer->lexbuf + start,
  3590.                     lexer->lexsize - start - 1);
  3591.                 if (c == '>' || c == '[')
  3592.                 {
  3593.                     --(lexer->lexsize);
  3594.                     UngetChar(c, doc->docIn);
  3595.                 }
  3596.  
  3597.                 state = DT_INTERMEDIATE;
  3598.                 continue;
  3599.             }
  3600.             break;
  3601.         case DT_PUBLICSYSTEM:
  3602.             /* read PUBLIC/SYSTEM */
  3603.             if (IsWhite(c) || c == '>')
  3604.             {
  3605.                 char *attname = tmbstrndup(lexer->lexbuf + start,
  3606.                     lexer->lexsize - start - 1);
  3607.                 hasfpi = !(tmbstrcasecmp(attname, "SYSTEM") == 0);
  3608.  
  3609.                 MemFree(attname);
  3610.  
  3611.                 /* todo: report an error if SYSTEM/PUBLIC not uppercase */
  3612.  
  3613.                 if (c == '>')
  3614.                 {
  3615.                     --(lexer->lexsize);
  3616.                     UngetChar(c, doc->docIn);
  3617.                 }
  3618.  
  3619.                 state = DT_INTERMEDIATE;
  3620.                 continue;
  3621.             }
  3622.             break;
  3623.         case DT_QUOTEDSTRING:
  3624.             /* read quoted string */
  3625.             if (c == delim)
  3626.             {
  3627.                 char *value = tmbstrndup(lexer->lexbuf + start,
  3628.                     lexer->lexsize - start - 1);
  3629.                 AttVal* att = AddAttribute(doc, node, hasfpi ? "PUBLIC" : "SYSTEM", value);
  3630.                 MemFree(value);
  3631.                 att->delim = delim;
  3632.                 hasfpi = no;
  3633.                 state = DT_INTERMEDIATE;
  3634.                 delim = 0;
  3635.                 continue;
  3636.             }
  3637.             break;
  3638.         case DT_INTSUBSET:
  3639.             /* read internal subset */
  3640.             if (c == ']')
  3641.             {
  3642.                 Node* subset;
  3643.                 lexer->txtstart = start;
  3644.                 lexer->txtend = lexer->lexsize - 1;
  3645.                 subset = TextToken(lexer);
  3646.                 InsertNodeAtEnd(node, subset);
  3647.                 state = DT_INTERMEDIATE;
  3648.             }
  3649.             break;
  3650.         }
  3651.     }
  3652.  
  3653.     /* document type declaration not finished */
  3654.     return NULL;
  3655. }
  3656.